Skip to content

Commit a7ab103

Browse files
ShadyBoukharyumar456
authored andcommitted
Implemented exception handling in the native code that throws Java exceptions
1 parent 123fc39 commit a7ab103

File tree

11 files changed

+259
-8
lines changed

11 files changed

+259
-8
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ ADD_JAR(${AF_JAR}
2929
com/arrayfire/Image.java
3030
com/arrayfire/Signal.java
3131
com/arrayfire/Util.java
32+
com/util/JNIException.java
33+
com/arrayfire/ArrayFireException.java
3234
)
3335

3436
ADD_DEPENDENCIES(${AF_JAR} ${AF_LIB})

com/arrayfire/Array.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.arrayfire;
22

3-
public class Array extends ArrayFire implements AutoCloseable{
3+
public class Array extends ArrayFire implements AutoCloseable {
44

55
public static final int FloatType = 0;
66
public static final int FloatComplexType = 1;
@@ -13,6 +13,8 @@ public class Array extends ArrayFire implements AutoCloseable{
1313
private native static int[] getDims(long ref);
1414
private native static int getType(long ref);
1515

16+
private native static long createIdentityArray(int[] dims, int type);
17+
1618
// Global reference to JVM object
1719
// to persist between JNI calls
1820
protected long ref;
@@ -147,6 +149,20 @@ public static Array constant(double val, int[] dims, int type) throws Exception
147149
return array;
148150
}
149151

152+
public static Array identity(int[] dims, int type) throws Exception {
153+
int[] adims = Array.dim4(dims);
154+
long ref = createIdentityArray(adims, FloatType);
155+
if (ref == 0) { throw new Exception("Failed to create Array"); }
156+
return new Array(ref);
157+
}
158+
159+
160+
public static Array identity(int[] dims) throws Exception {
161+
int[] adims = Array.dim4(dims);
162+
long ref = createIdentityArray(adims, FloatType);
163+
return new Array(ref);
164+
}
165+
150166
@Override
151167
public void close() throws Exception {
152168
System.out.println("Destroying");

com/arrayfire/ArrayFire.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.arrayfire;
2+
public class ArrayFire {
3+
static {
4+
System.loadLibrary("af_java");
5+
}
6+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.arrayfire;
2+
import java.util.Collections;
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
6+
import com.util.JNIException;
7+
8+
public class ArrayFireException extends JNIException {
9+
private static final Map<Integer, String> errorCodes;
10+
11+
static {
12+
@SuppressWarnings("serial")
13+
final Map<Integer, String> map = new HashMap<>() {{
14+
put(0, "Success");
15+
put(101, "AF_ERR_NO_MEM: The system or device ran out of memory.");
16+
put(102, "AF_ERR_DRIVER: There was an error in the device driver.");
17+
put(103, "AF_ERR_RUNTIME: There was an error in the runtime environment.");
18+
put(201, "AF_ERR_INVALID_ARRAY: The input variable is not a valid Array object.");
19+
put(202, "AF_ERR_ARG: One of the function arguments is incorrect.");
20+
put(203, "AF_ERR_SIZE: The size is incorrect.");
21+
put(204, "AF_ERR_TYPE: The type is not supported by this function.");
22+
put(205, "AF_ERR_DIFF_TYPE: The types of the input arrays are incompatible.");
23+
put(207, "AF_ERR_BATCH: Function does not support GFOR / batch mode.");
24+
put(208, "AF_ERR_DEVICE: Input does not belong to the current device.");
25+
put(301, "AF_ERR_NOT_SUPPORTED: The option is not supported.");
26+
put(302, "AF_ERR_NOT_CONFIGURED: The build of ArrayFire does not support this feature.");
27+
put(303, "AF_ERR_NONFREE: The build of ArrayFire is not compiled with \"nonfree\" algorithms");
28+
put(401, "AF__ERR_NO_DBL: This device does not support double");
29+
put(402, "AF_ERR_NO_GFX: This build of ArrayFire was not built with graphics or this device does not support graphics.");
30+
put(501, "AF_ERR_LOAD_LIB: There was an error loading the libraries.");
31+
put(502, "AF_ERR_LOAD_SYM: There was an error when loading the symbols.");
32+
put(503, "AF_ERR_ARR_BKND_MISMATCH: There was a mismatch between the input array and the active backend.");
33+
put(998, "AF_ERR_INTERNAL: There was an internal error either in ArrayFire or in a project upstream.");
34+
put(999, "AF_ERR_UNKNOWN: An unknown error has occured.");
35+
}
36+
};
37+
errorCodes = Collections.unmodifiableMap(map);
38+
}
39+
40+
public ArrayFireException(int code, String message) {
41+
super(String.format("%s %s (code: %d)", errorCodes.get(code), message, code ));
42+
}
43+
}

com/util/JNIException.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.util;
2+
3+
public class JNIException extends Exception {
4+
public JNIException(String message) {
5+
super(message);
6+
}
7+
/**
8+
* Called by native code during construction to set the location.
9+
*/
10+
public void setLocation(String functionName, String file, int line) {
11+
JNIException.addStackTraceElement(this, functionName, file, line);
12+
}
13+
14+
/**
15+
* Pushes a stack trace element onto the existing stack trace of the throwable.
16+
*/
17+
public static void addStackTraceElement​(Throwable t, String functionName,
18+
String file, int line) {
19+
StackTraceElement[] currentStack = t​.getStackTrace();
20+
StackTraceElement[] newStack = new StackTraceElement[currentStack​.length + 1];
21+
System.arraycopy(currentStack, 0, newStack, 1, currentStack​.length);
22+
file = file.replace('\\', '/');
23+
if (file.lastIndexOf('/') > -1) {
24+
file = file.substring(file​.lastIndexOf('/') + 1);
25+
}
26+
newStack[0] = new StackTraceElement("<native>", functionName, file, line);
27+
t​.setStackTrace(newStack);
28+
}
29+
}

examples/HelloWorld.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ public static void main(String[] args) {
3636
Array A = new Array(dims, left);
3737
Array B = new Array(dims, right);
3838
Array C = new Array();
39+
Array D = Array.identity(new int[] {0, 5}, Array.FloatType);
40+
float[] values = D.getFloatArray();
41+
for (int x = 0; x < 5; x++) {
42+
for (int y = 0; y < 5; y++) {
43+
System.out.print(Float.toString(values[5 * x + y]) + " ");
44+
}
45+
46+
System.out.println();
47+
}
48+
3949

4050
// Do vector addition on the device
4151
Arith.add(C, A, B);
@@ -54,10 +64,11 @@ public static void main(String[] args) {
5464
A.close();
5565
B.close();
5666
C.close();
67+
D.close();
5768

5869
} catch (Exception e) {
5970
System.out.println("Failed to use ArrayFire");
60-
System.out.println(e.getMessage());
71+
e.printStackTrace();
6172
}
6273

6374
}

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ ENDIF()
1111
INCLUDE_DIRECTORIES(${JNI_INCLUDE_DIRS} ${ArrayFire_INCLUDE_DIRS})
1212

1313
ADD_LIBRARY(${AF_LIB} SHARED
14+
exception.h
1415
jni_helper.h
16+
exception.cpp
1517
algorithm.cpp
1618
arith.cpp
1719
array.cpp

src/array.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ BEGIN_EXTERN_C
66

77
JNIEXPORT void JNICALL ARRAY_FUNC(destroyArray)(JNIEnv *env, jclass clazz, jlong ref)
88
{
9-
AF_TO_JAVA(af_release_array(ARRAY(ref)));
9+
THROWS(af_release_array(ARRAY(ref)));
1010
}
1111

1212
JNIEXPORT jintArray JNICALL ARRAY_FUNC(getDims)(JNIEnv *env, jclass clazz, jlong ref)
@@ -17,7 +17,7 @@ JNIEXPORT jintArray JNICALL ARRAY_FUNC(getDims)(JNIEnv *env, jclass clazz, jlong
1717
}
1818

1919
dim_t dims[4];
20-
AF_TO_JAVA(af_get_dims(dims + 0,
20+
THROWS(af_get_dims(dims + 0,
2121
dims + 1,
2222
dims + 2,
2323
dims + 3,
@@ -36,8 +36,18 @@ JNIEXPORT jintArray JNICALL ARRAY_FUNC(getDims)(JNIEnv *env, jclass clazz, jlong
3636
JNIEXPORT jint JNICALL ARRAY_FUNC(getType)(JNIEnv *env, jclass clazz, jlong ref)
3737
{
3838
af_dtype ty = f32;
39-
AF_TO_JAVA(af_get_type(&ty, ARRAY(ref)));
39+
THROWS(af_get_type(&ty, ARRAY(ref)));
4040
return ty;
4141
}
4242

43+
JNIEXPORT jlong JNICALL ARRAY_FUNC(createIdentityArray)(JNIEnv *env, jclass clazz,
44+
jintArray dims, jint type) {
45+
af_array ret = 0;
46+
jint *dimptr = env->GetIntArrayElements(dims, 0);
47+
dim_t tdims[4] = {dimptr[0], dimptr[1], dimptr[2], dimptr[3]};
48+
env->ReleaseIntArrayElements(dims, dimptr, 0);
49+
THROWS(af_identity(&ret, 4, tdims, (af_dtype)(type)));
50+
return JLONG(ret);
51+
}
52+
4353
END_EXTERN_C

src/exception.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#include "exception.h"
2+
3+
enum class JavaType {
4+
Int,
5+
Byte,
6+
Char,
7+
Double,
8+
Float,
9+
Long,
10+
String,
11+
Short,
12+
Void,
13+
Boolean
14+
};
15+
16+
static const char* mapJavaTypeToString(JavaType type) {
17+
switch (type) {
18+
case JavaType::Int: return "I";
19+
case JavaType::Byte: return "B";
20+
case JavaType::Char: return "C";
21+
case JavaType::Double: return "D";
22+
case JavaType::Float: return "F";
23+
case JavaType::Long: return "J";
24+
case JavaType::String: return "Ljava/lang/String;";
25+
case JavaType::Short: return "S";
26+
case JavaType::Void: return "V";
27+
case JavaType::Boolean: return "B";
28+
default: throw std::logic_error("Unknown Java Type");
29+
}
30+
}
31+
32+
static std::string generateFunctionSignature(JavaType returnType,
33+
std::initializer_list<JavaType> params) {
34+
std::string signature = "(";
35+
for (const auto& param : params) { signature += mapJavaTypeToString(param); }
36+
signature += ")";
37+
signature += mapJavaTypeToString(returnType);
38+
return signature;
39+
}
40+
41+
void JavaException::throwArrayFireException(JNIEnv *env, const char *functionName, const char *file,
42+
const int line, const int code) {
43+
//Find and instantiate an ArrayFireException
44+
jclass exceptionClass = env->FindClass("com/arrayfire/ArrayFireException");
45+
if (env->ExceptionCheck()) {
46+
env->ExceptionDescribe();
47+
}
48+
49+
const std::string constructorSig = generateFunctionSignature(JavaType::Void, {
50+
JavaType::Int, JavaType::String
51+
});
52+
jmethodID constructor = env->GetMethodID(exceptionClass, "<init>", constructorSig.c_str());
53+
54+
jthrowable exception = reinterpret_cast<jthrowable>(
55+
env->NewObject(exceptionClass,
56+
constructor,
57+
code,
58+
env->NewStringUTF("Some custom message here.")));
59+
60+
// Find setLocation method and call it with
61+
// the function name, file and line parameters
62+
const std::string setLocationSig = generateFunctionSignature(JavaType::Void, {
63+
JavaType::String, JavaType::String, JavaType::Int
64+
});
65+
jmethodID setLocationID = env->GetMethodID(exceptionClass, "setLocation", setLocationSig.c_str());
66+
env->CallVoidMethod(exception, setLocationID, env->NewStringUTF(functionName),
67+
env->NewStringUTF(file), line);
68+
69+
env->Throw(exception);
70+
env->DeleteLocalRef(exceptionClass);
71+
}

src/exception.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#include <stdexcept>
2+
#include <jni.h>
3+
#include <arrayfire.h>
4+
#include <assert.h>
5+
6+
class ThrownJavaException : std::runtime_error {
7+
8+
public:
9+
ThrownJavaException() : std::runtime_error("") {}
10+
ThrownJavaException(const std::string &msg) : std::runtime_error(msg) {}
11+
12+
static inline void assert_no_exception(JNIEnv *env) {
13+
if (env->ExceptionCheck() == JNI_TRUE)
14+
throw ThrownJavaException("assert_no_exception");
15+
}
16+
};
17+
18+
// Throws a Java exception using the full path
19+
//"java/lang/NoSuchFieldException"
20+
//"java/lang/NullPointerException"
21+
//"java/security/InvalidParameterException"
22+
class JavaException : public ThrownJavaException {
23+
public:
24+
JavaException(JNIEnv *env, const char *type = "", const char *message = "")
25+
: ThrownJavaException(type + std::string(" ") + message) {
26+
27+
jclass exceptionClass = env->FindClass(type);
28+
if (exceptionClass != NULL) {
29+
env->ThrowNew(exceptionClass, message);
30+
}
31+
}
32+
static void throwArrayFireException(JNIEnv *env, const char *functionName, const char *file,
33+
const int line, const int code);
34+
};
35+
36+
#define CATCH_AND_THROW_JAVA(env) \
37+
catch (const ThrownJavaException &) { \
38+
} catch (const std::bad_alloc &rhs) { \
39+
JavaException(env, "java/lang/OutOfMemoryError", rhs.what()); \
40+
} catch (const std::ios_base::failure &rhs) { \
41+
JavaException(env, "java/io/IOException", rhs.what()); \
42+
} catch(const std::bad_cast &e) { \
43+
JavaException(env, "java/lang/ClassCastException", e.what()); \
44+
} catch (const std::exception &e) { \
45+
JavaException(env, "java/lang/Error", e.what()); \
46+
} catch (...) { \
47+
JavaException(env, "java/lang/Error", "Unknown exception type");\
48+
} \
49+
50+
#define ARRAYFIRE_THROWS(err) \
51+
if (err != AF_SUCCESS) { \
52+
JavaException::throwArrayFireException(env, __func__, __FILE__, \
53+
__LINE__, (int)err); \
54+
}
55+
56+
#define THROWS(fn) \
57+
try { \
58+
ARRAYFIRE_THROWS(fn) \
59+
} \
60+
CATCH_AND_THROW_JAVA(env) \
61+

0 commit comments

Comments
 (0)