From b4997f637910efb62812567f1e36e6e67b71910d Mon Sep 17 00:00:00 2001 From: md_5 Date: Sun, 8 Feb 2015 09:11:52 +1100 Subject: [PATCH] Add better exception handling for native code. --- native/compile-native.sh | 2 +- native/src/main/c/NativeCompressImpl.cpp | 32 +++++++++--------- .../md_5/bungee/jni/NativeCodeException.java | 10 ++++++ native/src/main/resources/native-cipher.so | Bin 6648 -> 8384 bytes native/src/main/resources/native-compress.so | Bin 12216 -> 13160 bytes 5 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 native/src/main/java/net/md_5/bungee/jni/NativeCodeException.java diff --git a/native/compile-native.sh b/native/compile-native.sh index 986d5595..f6e6a35a 100755 --- a/native/compile-native.sh +++ b/native/compile-native.sh @@ -1,6 +1,6 @@ #!/bin/sh -CXX="g++ -nostdlib -std=c++11 -shared -fPIC -O3 -Wall -Werror -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/" +CXX="g++ -std=c++11 -shared -fPIC -O3 -Wall -Werror -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/" $CXX src/main/c/NativeCipherImpl.cpp -o src/main/resources/native-cipher.so -lcrypto $CXX src/main/c/NativeCompressImpl.cpp -o src/main/resources/native-compress.so -lz diff --git a/native/src/main/c/NativeCompressImpl.cpp b/native/src/main/c/NativeCompressImpl.cpp index 0dc668c9..9bda6300 100644 --- a/native/src/main/c/NativeCompressImpl.cpp +++ b/native/src/main/c/NativeCompressImpl.cpp @@ -1,20 +1,9 @@ -#include +#include #include #include "net_md_5_bungee_jni_zlib_NativeCompressImpl.h" -using namespace std; typedef unsigned char byte; -jint throwException(JNIEnv *env, string message) { - jclass exClass = env->FindClass("java/lang/RuntimeException"); - // Can never actually happen on a sane JVM, but better be safe anyway - if (exClass == NULL) { - return -1; - } - - return env->ThrowNew(exClass, message.c_str()); -} - static jfieldID consumedID; static jfieldID finishedID; @@ -24,12 +13,23 @@ void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_initFields(JNIEnv finishedID = env->GetFieldID(clazz, "finished", "Z"); } +jint throwException(JNIEnv *env, const char* message, int err) { + // These can't be static for some unknown reason + jclass exceptionClass = env->FindClass("net/md_5/bungee/jni/NativeCodeException"); + jmethodID exceptionInitID = env->GetMethodID(exceptionClass, "", "(Ljava/lang/String;I)V"); + + jstring jMessage = env->NewStringUTF(message); + + jthrowable throwable = (jthrowable) env->NewObject(exceptionClass, exceptionInitID, jMessage, err); + return env->Throw(throwable); +} + void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_reset(JNIEnv* env, jobject obj, jlong ctx, jboolean compress) { z_stream* stream = (z_stream*) ctx; int ret = (compress) ? deflateReset(stream) : inflateReset(stream); if (ret != Z_OK) { - throwException(env, "Could not reset z_stream: " + to_string(ret)); + throwException(env, "Could not reset z_stream", ret); } } @@ -40,7 +40,7 @@ void JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_end(JNIEnv* env, free(stream); if (ret != Z_OK) { - throwException(env, "Could not free z_stream: " + to_string(ret)); + throwException(env, "Could not free z_stream: ", ret); } } @@ -49,7 +49,7 @@ jlong JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_init(JNIEnv* env int ret = (compress) ? deflateInit(stream, level) : inflateInit(stream); if (ret != Z_OK) { - throwException(env, "Could not init z_stream: " + to_string(ret)); + throwException(env, "Could not init z_stream", ret); } return (jlong) stream; @@ -73,7 +73,7 @@ jint JNICALL Java_net_md_15_bungee_jni_zlib_NativeCompressImpl_process(JNIEnv* e case Z_OK: break; default: - throwException(env, "Unknown z_stream return code: " + to_string(ret)); + throwException(env, "Unknown z_stream return code", ret); } env->SetIntField(obj, consumedID, inLength - stream->avail_in); diff --git a/native/src/main/java/net/md_5/bungee/jni/NativeCodeException.java b/native/src/main/java/net/md_5/bungee/jni/NativeCodeException.java new file mode 100644 index 00000000..0020c54b --- /dev/null +++ b/native/src/main/java/net/md_5/bungee/jni/NativeCodeException.java @@ -0,0 +1,10 @@ +package net.md_5.bungee.jni; + +public class NativeCodeException extends Exception +{ + + public NativeCodeException(String message, int reason) + { + super( message + " : " + reason ); + } +} diff --git a/native/src/main/resources/native-cipher.so b/native/src/main/resources/native-cipher.so index c954fa7388e3f0457708b0c6224b2673b70085db..d50d7bbc08b308d99be65083de935d4558982e52 100755 GIT binary patch literal 8384 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 NI)l7wY0#pwzW@=ebVvXI diff --git a/native/src/main/resources/native-compress.so b/native/src/main/resources/native-compress.so index 5b9416486292f91d1efa9317f3e74678d238e2c7..c41bc583282d2f4ea55d81f28c0f67b5c5cb2bd3 100755 GIT binary patch literal 13160 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 literal 12216 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