Skip to content

Commit

Permalink
mobile: Part 3: Update JNI usages with JniHelper (envoyproxy#30752)
Browse files Browse the repository at this point in the history
mobile: Part 3 Update JNI usages with JniHelper

This updates the following methods:
- `SetObjectArrayElement`
- `SetByteArrayRegion`

This PR also adds missing `Set<Primitive>ArrayRegion` methods into
`JniHelper`.

Signed-off-by: Fredy Wijaya <[email protected]>
  • Loading branch information
fredyw authored Nov 6, 2023
1 parent 684cdd3 commit 15166ea
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 7 deletions.
16 changes: 16 additions & 0 deletions mobile/library/common/jni/jni_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,22 @@ PrimitiveArrayCriticalUniquePtr JniHelper::getPrimitiveArrayCritical(jarray arra
return result;
}

#define DEFINE_SET_ARRAY_REGION(JAVA_TYPE, JNI_ARRAY_TYPE, JNI_ELEMENT_TYPE) \
void JniHelper::set##JAVA_TYPE##ArrayRegion(JNI_ARRAY_TYPE array, jsize start, jsize length, \
const JNI_ELEMENT_TYPE* buffer) { \
env_->Set##JAVA_TYPE##ArrayRegion(array, start, length, buffer); \
rethrowException(); \
}

DEFINE_SET_ARRAY_REGION(Byte, jbyteArray, jbyte)
DEFINE_SET_ARRAY_REGION(Char, jcharArray, jchar)
DEFINE_SET_ARRAY_REGION(Short, jshortArray, jshort)
DEFINE_SET_ARRAY_REGION(Int, jintArray, jint)
DEFINE_SET_ARRAY_REGION(Long, jlongArray, jlong)
DEFINE_SET_ARRAY_REGION(Float, jfloatArray, jfloat)
DEFINE_SET_ARRAY_REGION(Double, jdoubleArray, jdouble)
DEFINE_SET_ARRAY_REGION(Boolean, jbooleanArray, jboolean)

#define DEFINE_CALL_METHOD(JAVA_TYPE, JNI_TYPE) \
JNI_TYPE JniHelper::call##JAVA_TYPE##Method(jobject object, jmethodID method_id, ...) { \
va_list args; \
Expand Down
18 changes: 18 additions & 0 deletions mobile/library/common/jni/jni_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,24 @@ class JniHelper {

PrimitiveArrayCriticalUniquePtr getPrimitiveArrayCritical(jarray array, jboolean* is_copy);

/**
* Sets a region of an `array` from a `buffer` with the specified `start` index and `length`.
*
* https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#setprimitivetypearrayregion-routines
*/
#define DECLARE_SET_ARRAY_REGION(JAVA_TYPE, JNI_ARRAY_TYPE, JNI_ELEMENT_TYPE) \
void set##JAVA_TYPE##ArrayRegion(JNI_ARRAY_TYPE array, jsize start, jsize length, \
const JNI_ELEMENT_TYPE* buffer);

DECLARE_SET_ARRAY_REGION(Byte, jbyteArray, jbyte)
DECLARE_SET_ARRAY_REGION(Char, jcharArray, jchar)
DECLARE_SET_ARRAY_REGION(Short, jshortArray, jshort)
DECLARE_SET_ARRAY_REGION(Int, jintArray, jint)
DECLARE_SET_ARRAY_REGION(Long, jlongArray, jlong)
DECLARE_SET_ARRAY_REGION(Float, jfloatArray, jfloat)
DECLARE_SET_ARRAY_REGION(Double, jdoubleArray, jdouble)
DECLARE_SET_ARRAY_REGION(Boolean, jbooleanArray, jboolean)

/** A macro to create `Call<Type>Method` helper function. */
#define DECLARE_CALL_METHOD(JAVA_TYPE, JNI_TYPE) \
JNI_TYPE call##JAVA_TYPE##Method(jobject object, jmethodID method_id, ...);
Expand Down
6 changes: 3 additions & 3 deletions mobile/library/common/jni/jni_interface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -280,11 +280,11 @@ static void* jvm_on_headers(const char* method, const Envoy::Types::ManagedEnvoy
jobject j_status = jni_helper.getEnv()->NewObject(jcls_int.get(), jmid_intInit, 0);
// Set status to "0" (FilterHeadersStatus::Continue). Signal that the intent
// is to continue the iteration of the filter chain.
jni_helper.getEnv()->SetObjectArrayElement(noopResult, 0, j_status);
jni_helper.setObjectArrayElement(noopResult, 0, j_status);

// Since the "on headers" call threw an exception set input headers as output headers.
jni_helper.getEnv()->SetObjectArrayElement(
noopResult, 1, Envoy::JNI::ToJavaArrayOfObjectArray(jni_helper, headers));
jni_helper.setObjectArrayElement(noopResult, 1,
Envoy::JNI::ToJavaArrayOfObjectArray(jni_helper, headers));

return noopResult;
}
Expand Down
8 changes: 4 additions & 4 deletions mobile/library/common/jni/jni_utility.cc
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@ jobjectArray ToJavaArrayOfObjectArray(JniHelper& jni_helper,
jbyteArray key = native_data_to_array(jni_helper, map.get().entries[i].key);
jbyteArray value = native_data_to_array(jni_helper, map.get().entries[i].value);

jni_helper.getEnv()->SetObjectArrayElement(javaArray, 2 * i, key);
jni_helper.getEnv()->SetObjectArrayElement(javaArray, 2 * i + 1, value);
jni_helper.setObjectArrayElement(javaArray, 2 * i, key);
jni_helper.setObjectArrayElement(javaArray, 2 * i + 1, value);
}

return javaArray;
Expand All @@ -284,15 +284,15 @@ jobjectArray ToJavaArrayOfByteArray(JniHelper& jni_helper, const std::vector<std
for (size_t i = 0; i < v.size(); ++i) {
jbyteArray byte_array =
ToJavaByteArray(jni_helper, reinterpret_cast<const uint8_t*>(v[i].data()), v[i].length());
jni_helper.getEnv()->SetObjectArrayElement(joa, i, byte_array);
jni_helper.setObjectArrayElement(joa, i, byte_array);
}
return joa;
}

jbyteArray ToJavaByteArray(JniHelper& jni_helper, const uint8_t* bytes, size_t len) {
jbyteArray byte_array = jni_helper.getEnv()->NewByteArray(len);
const jbyte* jbytes = reinterpret_cast<const jbyte*>(bytes);
jni_helper.getEnv()->SetByteArrayRegion(byte_array, /*start=*/0, len, jbytes);
jni_helper.setByteArrayRegion(byte_array, /*start=*/0, len, jbytes);
return byte_array;
}

Expand Down
18 changes: 18 additions & 0 deletions mobile/test/common/jni/jni_helper_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,24 @@ Java_io_envoyproxy_envoymobile_jni_JniHelperTest_setObjectArrayElement(JNIEnv* e
jni_helper.setObjectArrayElement(array, index, value);
}

#define DEFINE_JNI_SET_ARRAY_REGION(JAVA_TYPE, JNI_TYPE) \
extern "C" JNIEXPORT void JNICALL \
Java_io_envoyproxy_envoymobile_jni_JniHelperTest_set##JAVA_TYPE##ArrayRegion( \
JNIEnv* env, jclass, JNI_TYPE array, jsize start, jsize length, JNI_TYPE buffer) { \
Envoy::JNI::JniHelper jni_helper(env); \
auto c_buffer = jni_helper.get##JAVA_TYPE##ArrayElements(buffer, nullptr); \
env->Set##JAVA_TYPE##ArrayRegion(array, start, length, c_buffer.get()); \
}

DEFINE_JNI_SET_ARRAY_REGION(Byte, jbyteArray)
DEFINE_JNI_SET_ARRAY_REGION(Char, jcharArray)
DEFINE_JNI_SET_ARRAY_REGION(Short, jshortArray)
DEFINE_JNI_SET_ARRAY_REGION(Int, jintArray)
DEFINE_JNI_SET_ARRAY_REGION(Long, jlongArray)
DEFINE_JNI_SET_ARRAY_REGION(Float, jfloatArray)
DEFINE_JNI_SET_ARRAY_REGION(Double, jdoubleArray)
DEFINE_JNI_SET_ARRAY_REGION(Boolean, jbooleanArray)

#define DEFINE_JNI_CALL_METHOD(JAVA_TYPE, JNI_TYPE) \
extern "C" JNIEXPORT JNI_TYPE JNICALL \
Java_io_envoyproxy_envoymobile_jni_JniHelperTest_call##JAVA_TYPE##Method( \
Expand Down
76 changes: 76 additions & 0 deletions mobile/test/java/io/envoyproxy/envoymobile/jni/JniHelperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ public static native Object[] newObjectArray(int length, Class<?> elementClass,
Object initialElement);
public static native Object getObjectArrayElement(Object[] array, int index);
public static native void setObjectArrayElement(Object[] array, int index, Object value);
public static native void setByteArrayRegion(byte[] array, int start, int index, byte[] buffer);
public static native void setCharArrayRegion(char[] array, int start, int index, char[] buffer);
public static native void setShortArrayRegion(short[] array, int start, int index,
short[] buffer);
public static native void setIntArrayRegion(int[] array, int start, int index, int[] buffer);
public static native void setLongArrayRegion(long[] array, int start, int index, long[] buffer);
public static native void setFloatArrayRegion(float[] array, int start, int index,
float[] buffer);
public static native void setDoubleArrayRegion(double[] array, int start, int index,
double[] buffer);
public static native void setBooleanArrayRegion(boolean[] array, int start, int index,
boolean[] buffer);
public static native byte callByteMethod(Class<?> clazz, Object instance, String name,
String signature);
public static native char callCharMethod(Class<?> clazz, Object instance, String name,
Expand Down Expand Up @@ -187,6 +199,70 @@ public void testSetObjectArrayElement() {
assertThat(array).isEqualTo(new Object[] {1, 200, 3});
}

@Test
public void testSetByteArrayRegion() {
byte[] array = new byte[] {1, 0, 0, 0, 5};
byte[] buffer = new byte[] {2, 3, 4};
setByteArrayRegion(array, 1, 3, buffer);
assertThat(array).isEqualTo(new byte[] {1, 2, 3, 4, 5});
}

@Test
public void testSetCharArrayRegion() {
char[] array = new char[] {'a', ' ', ' ', ' ', 'e'};
char[] buffer = new char[] {'b', 'c', 'd'};
setCharArrayRegion(array, 1, 3, buffer);
assertThat(array).isEqualTo(new char[] {'a', 'b', 'c', 'd', 'e'});
}

@Test
public void testSetShortArrayRegion() {
short[] array = new short[] {1, 0, 0, 0, 5};
short[] buffer = new short[] {2, 3, 4};
setShortArrayRegion(array, 1, 3, buffer);
assertThat(array).isEqualTo(new short[] {1, 2, 3, 4, 5});
}

@Test
public void testSetIntArrayRegion() {
int[] array = new int[] {1, 0, 0, 0, 5};
int[] buffer = new int[] {2, 3, 4};
setIntArrayRegion(array, 1, 3, buffer);
assertThat(array).isEqualTo(new int[] {1, 2, 3, 4, 5});
}

@Test
public void testSetLongArrayRegion() {
long[] array = new long[] {1, 0, 0, 0, 5};
long[] buffer = new long[] {2, 3, 4};
setLongArrayRegion(array, 1, 3, buffer);
assertThat(array).isEqualTo(new long[] {1, 2, 3, 4, 5});
}

@Test
public void testSetFloatArrayRegion() {
float[] array = new float[] {1, 0, 0, 0, 5};
float[] buffer = new float[] {2, 3, 4};
setFloatArrayRegion(array, 1, 3, buffer);
assertThat(array).isEqualTo(new float[] {1, 2, 3, 4, 5});
}

@Test
public void testSetDoubleArrayRegion() {
double[] array = new double[] {1, 0, 0, 0, 5};
double[] buffer = new double[] {2, 3, 4};
setDoubleArrayRegion(array, 1, 3, buffer);
assertThat(array).isEqualTo(new double[] {1, 2, 3, 4, 5});
}

@Test
public void testSetBooleanArrayRegion() {
boolean[] array = new boolean[] {true, false, false, false, true};
boolean[] buffer = new boolean[] {true, true, true};
setBooleanArrayRegion(array, 1, 3, buffer);
assertThat(array).isEqualTo(new boolean[] {true, true, true, true, true});
}

@Test
public void testCallByteMethod() {
assertThat(callByteMethod(JniHelperTest.class, this, "byteMethod", "()B")).isEqualTo((byte)1);
Expand Down

0 comments on commit 15166ea

Please sign in to comment.