Skip to content

Commit

Permalink
More safety on JNI
Browse files Browse the repository at this point in the history
  • Loading branch information
Danielku15 committed Nov 12, 2023
1 parent dd99006 commit 8fd278e
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 5 deletions.
33 changes: 33 additions & 0 deletions lib/java/jni/include/JniHelper.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
#include <jni.h>

#define STRINGIFY(s) _STRINGIFY(s)
#define _STRINGIFY(s) #s

inline jlong get_handle(JNIEnv *env, jobject instance)
{
jclass cls = env->FindClass("alphaTab/alphaSkia/AlphaSkiaNative");
if (!cls)
{
env->ThrowNew(env->FindClass("java/lang/IllegalStateException"), "Could not find class alphaTab.alphaSkia.AlphaSkiaNative");
return 0;
}
jfieldID handleFieldId = env->GetFieldID(cls, "handle", "J");
if (!handleFieldId)
{
env->ThrowNew(env->FindClass("java/lang/IllegalStateException"), "Could not find field handle in class alphaTab.alphaSkia.AlphaSkiaNative");
return 0;
}
return env->GetLongField(instance, handleFieldId);
}
inline void set_handle(JNIEnv *env, jobject instance, jlong handle)
Expand All @@ -12,3 +25,23 @@ inline void set_handle(JNIEnv *env, jobject instance, jlong handle)
jfieldID handleFieldId = env->GetFieldID(cls, "handle", "J");
env->SetLongField(instance, handleFieldId, handle);
}

#define CHECK_HANDLE_RETURN(handle, returnValue) \
if (!handle) \
{ \
if (!env->ExceptionCheck()) \
{ \
env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"), "Invalid object handle (" STRINGIFY(handle) ")"); \
} \
return returnValue; \
}

#define CHECK_HANDLE(handle) \
if (!handle) \
{ \
if (!env->ExceptionCheck()) \
{ \
env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"), "Invalid object handle (" STRINGIFY(handle) ")"); \
} \
return; \
}
76 changes: 73 additions & 3 deletions lib/java/jni/src/AlphaSkiaCanvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,50 +7,74 @@ extern "C"
JNIEXPORT jint JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_getColor(JNIEnv *env, jobject instance)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE_RETURN(canvas, 0)

return static_cast<jint>(alphaskia_canvas_get_color(canvas));
}

JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_setColor(JNIEnv *env, jobject instance, jint color)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)

alphaskia_canvas_set_color(canvas, static_cast<uint32_t>(color));
}

JNIEXPORT jfloat JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_getLineWidth(JNIEnv *env, jobject instance)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE_RETURN(canvas, 0.0f)

return static_cast<jfloat>(alphaskia_canvas_get_line_width(canvas));
}

JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_setLineWidth(JNIEnv *env, jobject instance, jfloat line_width)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)

alphaskia_canvas_set_line_width(canvas, static_cast<float>(line_width));
}

JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_close(JNIEnv *env, jobject instance)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)

alphaskia_canvas_free(canvas);
set_handle(env, instance, 0);
}

JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_beginRender(JNIEnv *env, jobject instance, jint width, jint height, jfloat renderScale)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)
alphaskia_canvas_begin_render(canvas, width, height, static_cast<float>(renderScale));
}

JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_drawImage(JNIEnv *env, jobject instance, jobject image, jfloat x, jfloat y, jfloat w, jfloat h)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)

alphaskia_image_t nativeImage = reinterpret_cast<alphaskia_image_t>(get_handle(env, image));
if (!canvas)
{
if (!env->ExceptionCheck())
{
env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"), "Invalid object handle");
}
return;
}

alphaskia_canvas_draw_image(canvas, nativeImage, static_cast<float>(x), static_cast<float>(y), static_cast<float>(w), static_cast<float>(h));
}

JNIEXPORT jobject JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_endRender(JNIEnv *env, jobject instance)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE_RETURN(canvas, nullptr)

alphaskia_image_t image = alphaskia_canvas_end_render(canvas);

jclass cls = env->FindClass("alphaTab/alphaSkia/AlphaSkiaImage");
Expand All @@ -61,91 +85,134 @@ extern "C"
JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_fillRect(JNIEnv *env, jobject instance, jfloat x, jfloat y, jfloat w, jfloat h)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)

alphaskia_canvas_fill_rect(canvas, x, y, w, h);
}
JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_strokeRect(JNIEnv *env, jobject instance, jfloat x, jfloat y, jfloat w, jfloat h)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)
alphaskia_canvas_stroke_rect(canvas, x, y, w, h);
}
JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_beginPath(JNIEnv *env, jobject instance)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)

alphaskia_canvas_begin_path(canvas);
}
JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_closePath(JNIEnv *env, jobject instance)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)

alphaskia_canvas_close_path(canvas);
}
JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_moveTo(JNIEnv *env, jobject instance, jfloat x, jfloat y)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)

alphaskia_canvas_move_to(canvas, x, y);
}
JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_lineTo(JNIEnv *env, jobject instance, jfloat x, jfloat y)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)

alphaskia_canvas_line_to(canvas, x, y);
}
JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_quadraticCurveTo(JNIEnv *env, jobject instance, jfloat cpx, jfloat cpy, jfloat x, jfloat y)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)

alphaskia_canvas_quadratic_curve_to(canvas, cpx, cpy, x, y);
}
JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_bezierCurveTo(JNIEnv *env, jobject instance, jfloat cp1x, jfloat cp1y, jfloat cp2x, jfloat cp2y, jfloat x, jfloat y)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)

alphaskia_canvas_bezier_curve_to(canvas, cp1x, cp1y, cp2x, cp2y, x, y);
}
JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_fillCircle(JNIEnv *env, jobject instance, jfloat x, jfloat y, jfloat r)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)

alphaskia_canvas_fill_circle(canvas, x, y, r);
}
JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_strokeCircle(JNIEnv *env, jobject instance, jfloat x, jfloat y, jfloat r)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)

alphaskia_canvas_stroke_circle(canvas, x, y, r);
}
JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_fill(JNIEnv *env, jobject instance)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)

alphaskia_canvas_fill(canvas);
}
JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_stroke(JNIEnv *env, jobject instance)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)

alphaskia_canvas_stroke(canvas);
}
JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_fillText(JNIEnv *env, jobject instance, jstring str, jobject typeface, jfloat font_size, jfloat x, jfloat y, jobject text_align, jobject baseline)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));

CHECK_HANDLE(canvas)

const jchar *nativeStr = env->GetStringChars(str, nullptr);

alphaskia_typeface_t nativeTypeface = reinterpret_cast<alphaskia_typeface_t>(get_handle(env, typeface));
CHECK_HANDLE(nativeTypeface)

jmethodID textAlignGetValue = env->GetMethodID(env->GetObjectClass(text_align), "getValue", "()I");
if (!textAlignGetValue)
{
if (!env->ExceptionCheck())
{
env->ThrowNew(env->FindClass("java/lang/IllegalStateException"), "Could not find 'int getValue' on text align");
}
return;
}

jmethodID baselineGetValue = env->GetMethodID(env->GetObjectClass(baseline), "getValue", "()I");
if (!baselineGetValue)
{
if (!env->ExceptionCheck())
{
env->ThrowNew(env->FindClass("java/lang/IllegalStateException"), "Could not find 'int getValue' on text baseline");
}
return;
}

alphaskia_text_align_t nativeTextAlign = static_cast<alphaskia_text_align_t>(env->CallIntMethod(text_align, textAlignGetValue));
alphaskia_text_baseline_t nativeBaseline = static_cast<alphaskia_text_baseline_t>(env->CallIntMethod(baseline, baselineGetValue));

alphaskia_canvas_fill_text(canvas, reinterpret_cast<const char16_t*>(nativeStr), nativeTypeface, static_cast<float>(font_size), static_cast<float>(x), static_cast<float>(y),
alphaskia_canvas_fill_text(canvas, reinterpret_cast<const char16_t *>(nativeStr), nativeTypeface, static_cast<float>(font_size), static_cast<float>(x), static_cast<float>(y),
nativeTextAlign, nativeBaseline);

env->ReleaseStringChars(str, nativeStr);
}
JNIEXPORT jfloat JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_measureText(JNIEnv *env, jobject instance, jstring str, jobject typeface, jfloat font_size)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE_RETURN(canvas, 0.0f)

const jchar *nativeStr = env->GetStringChars(str, nullptr);
alphaskia_typeface_t nativeTypeface = reinterpret_cast<alphaskia_typeface_t>(get_handle(env, typeface));
CHECK_HANDLE_RETURN(nativeTypeface, 0.0f)

float width = alphaskia_canvas_measure_text(canvas, reinterpret_cast<const char16_t*>(nativeStr), nativeTypeface, static_cast<float>(font_size));
float width = alphaskia_canvas_measure_text(canvas, reinterpret_cast<const char16_t *>(nativeStr), nativeTypeface, static_cast<float>(font_size));

env->ReleaseStringChars(str, nativeStr);

Expand All @@ -155,11 +222,14 @@ extern "C"
JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_beginRotate(JNIEnv *env, jobject instance, jfloat x, jfloat y, jfloat angle)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)

alphaskia_canvas_begin_rotate(canvas, static_cast<float>(x), static_cast<float>(y), static_cast<float>(angle));
}
JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_endRotate(JNIEnv *env, jobject instance)
{
alphaskia_canvas_t canvas = reinterpret_cast<alphaskia_canvas_t>(get_handle(env, instance));
CHECK_HANDLE(canvas)
alphaskia_canvas_end_rotate(canvas);
}
JNIEXPORT jlong JNICALL Java_alphaTab_alphaSkia_AlphaSkiaCanvas_alphaskiaCanvasAllocate(JNIEnv *env, jclass)
Expand Down
4 changes: 4 additions & 0 deletions lib/java/jni/src/AlphaSkiaData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ extern "C"
JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaData_close(JNIEnv *env, jobject instance)
{
jlong handle = get_handle(env, instance);
CHECK_HANDLE(handle)

alphaskia_data_t data = reinterpret_cast<alphaskia_data_t>(handle);
alphaskia_data_free(data);
set_handle(env, instance, 0);
Expand All @@ -25,6 +27,8 @@ extern "C"
JNIEXPORT jbyteArray JNICALL Java_alphaTab_alphaSkia_AlphaSkiaData_toArray(JNIEnv *env, jobject instance)
{
jlong handle = get_handle(env, instance);
CHECK_HANDLE_RETURN(handle, nullptr)

alphaskia_data_t data = reinterpret_cast<alphaskia_data_t>(handle);
uint8_t *raw = alphaskia_data_get_data(data);

Expand Down
11 changes: 10 additions & 1 deletion lib/java/jni/src/AlphaSkiaImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,39 @@ extern "C"
JNIEXPORT jint JNICALL Java_alphaTab_alphaSkia_AlphaSkiaImage_getWidth(JNIEnv *env, jobject instance)
{
jlong handle = get_handle(env, instance);
CHECK_HANDLE_RETURN(handle, 0)
return alphaskia_image_get_width(reinterpret_cast<alphaskia_image_t>(handle));
}

JNIEXPORT jint JNICALL Java_alphaTab_alphaSkia_AlphaSkiaImage_getHeight(JNIEnv *env, jobject instance)
{
jlong handle = get_handle(env, instance);
CHECK_HANDLE_RETURN(handle, 0)

return alphaskia_image_get_height(reinterpret_cast<alphaskia_image_t>(handle));
}

JNIEXPORT void JNICALL Java_alphaTab_alphaSkia_AlphaSkiaImage_close(JNIEnv *env, jobject instance)
{
jlong handle = get_handle(env, instance);
CHECK_HANDLE(handle)

alphaskia_image_free(reinterpret_cast<alphaskia_image_t>(handle));
set_handle(env, instance, 0);
}

JNIEXPORT jbyteArray JNICALL Java_alphaTab_alphaSkia_AlphaSkiaImage_readPixels(JNIEnv *env, jobject instance)
{
jlong handle = get_handle(env, instance);
CHECK_HANDLE_RETURN(handle, nullptr)

alphaskia_image_t image = reinterpret_cast<alphaskia_image_t>(handle);
jsize rowBytes = alphaskia_image_get_width(image) * sizeof(int32_t);
jbyteArray pixels = env->NewByteArray(
rowBytes * alphaskia_image_get_height(image));
jbyte *bytes = env->GetByteArrayElements(pixels, nullptr);

if ( alphaskia_image_read_pixels(image, reinterpret_cast<uint8_t *>(bytes), rowBytes) == 0)
if (alphaskia_image_read_pixels(image, reinterpret_cast<uint8_t *>(bytes), rowBytes) == 0)
{
env->ReleaseByteArrayElements(pixels, bytes, 0);
return nullptr;
Expand All @@ -47,6 +54,8 @@ extern "C"
JNIEXPORT jbyteArray JNICALL Java_alphaTab_alphaSkia_AlphaSkiaImage_toPng(JNIEnv *env, jobject instance)
{
jlong handle = get_handle(env, instance);
CHECK_HANDLE_RETURN(handle, nullptr)

alphaskia_image_t image = reinterpret_cast<alphaskia_image_t>(handle);
alphaskia_data_t data = alphaskia_image_encode_png(image);
uint8_t *raw = alphaskia_data_get_data(data);
Expand Down
8 changes: 7 additions & 1 deletion lib/java/jni/src/AlphaSkiaTypeface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,28 @@ extern "C"
JNIEXPORT jboolean JNICALL Java_alphaTab_alphaSkia_AlphaSkiaTypeface_isBold(JNIEnv *env, jobject instance)
{
jlong handle = get_handle(env, instance);
CHECK_HANDLE_RETURN(handle, static_cast<jboolean>(false))

uint8_t is_bold = alphaskia_typeface_is_bold(reinterpret_cast<alphaskia_typeface_t>(handle));
return static_cast<jboolean>(is_bold != 0);
}

JNIEXPORT jboolean JNICALL Java_alphaTab_alphaSkia_AlphaSkiaTypeface_isItalic(JNIEnv *env, jobject instance)
{
jlong handle = get_handle(env, instance);
CHECK_HANDLE_RETURN(handle, static_cast<jboolean>(false))

uint8_t is_italic = alphaskia_typeface_is_italic(reinterpret_cast<alphaskia_typeface_t>(handle));
return static_cast<jboolean>(is_italic != 0);
}

JNIEXPORT jstring JNICALL Java_alphaTab_alphaSkia_AlphaSkiaTypeface_loadFamilyName(JNIEnv *env, jclass, jlong handle)
{
CHECK_HANDLE_RETURN(handle, nullptr)

alphaskia_string_t family_name = alphaskia_typeface_get_family_name(reinterpret_cast<alphaskia_typeface_t>(handle));

const char* family_name_chars = alphaskia_string_get_utf8(family_name);
const char *family_name_chars = alphaskia_string_get_utf8(family_name);
jstring java_family_name = env->NewStringUTF(family_name_chars);

alphaskia_string_free(family_name);
Expand Down

0 comments on commit 8fd278e

Please sign in to comment.