Skip to content

Commit c72d55a

Browse files
author
FITOR
committed
implemented dynamic exception mapping through the @Platform-annotation
1 parent 0d068e4 commit c72d55a

File tree

5 files changed

+76
-11
lines changed

5 files changed

+76
-11
lines changed

src/main/java/org/bytedeco/javacpp/ClassProperties.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
package org.bytedeco.javacpp;
2424

25+
import org.bytedeco.javacpp.annotation.ExceptionMapper;
2526
import org.bytedeco.javacpp.annotation.Platform;
2627
import org.bytedeco.javacpp.tools.Logger;
2728

@@ -227,7 +228,19 @@ public void load(Class cls, boolean inherit) {
227228
if (!match) {
228229
continue;
229230
}
230-
if (p.exceptionMappings().length > 0) { exceptionMappings = p.exceptionMappings(); }
231+
232+
if (p.exceptionMappings().length > 0) {
233+
List<String> exceptionMappingsList = new LinkedList<>();
234+
for (ExceptionMapper mapper : p.exceptionMappings()) {
235+
final String cppException = mapper.cppException();
236+
final String javaException = mapper.javaExceptionClass().getName().replace('.', '/');
237+
238+
exceptionMappingsList.add(cppException);
239+
exceptionMappingsList.add(javaException);
240+
}
241+
exceptionMappings = exceptionMappingsList.toArray(new String[0]);
242+
}
243+
231244
if (p.pragma().length > 0) { pragma = p.pragma(); }
232245
if (p.define().length > 0) { define = p.define(); }
233246
if (p.exclude().length > 0) { exclude = p.exclude(); }
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.bytedeco.javacpp.annotation;
2+
3+
import java.lang.annotation.Retention;
4+
import java.lang.annotation.RetentionPolicy;
5+
import java.lang.annotation.Target;
6+
7+
/**
8+
* Exception Mapper maps a C/C++ exception to the given java exception.
9+
* Will overwrite any existing exception-mappings.
10+
*/
11+
@Retention(RetentionPolicy.RUNTIME)
12+
@Target({})
13+
public @interface ExceptionMapper {
14+
15+
/**
16+
* The C/C++ exception to be mapped (e.g.: std::runtime_error).
17+
*
18+
* @return A String representation of the C/C++ exception to be mapped.
19+
*/
20+
String cppException();
21+
22+
/**
23+
* The corresponding java-exception.
24+
*
25+
* @return The corresponding java-exception.
26+
*/
27+
Class<? extends Throwable> javaExceptionClass();
28+
}
29+

src/main/java/org/bytedeco/javacpp/annotation/Platform.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@
181181

182182
/**
183183
* Mappings for custom exceptions.
184-
* // TODO(Fitor) finish doc, also see whether you can replace String[] with a more fitting datatype (needs to be turned into String[] in {@link org.bytedeco.javacpp.ClassProperties eventually}
184+
* @return An array of {@link ExceptionMapper}, which determines the mapping of custom native exceptions to any javaException.
185185
*/
186-
String[] exceptionMappings() default "";
186+
ExceptionMapper[] exceptionMappings() default {};
187187
}

src/main/java/org/bytedeco/javacpp/tools/Generator.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -251,11 +251,6 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver
251251
out.println("#define " + s);
252252
}
253253

254-
out.println("FITORR");
255-
for(String s : clsProperties.get("platform.exceptionMappings")) {
256-
out.println(s);
257-
}
258-
259254
out.println();
260255
out.println("#ifdef _WIN32");
261256
out.println(" #define _JAVASOFT_JNI_MD_H_");
@@ -1089,7 +1084,31 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver
10891084
out.println("};");
10901085
out.println();
10911086

1087+
//////////////////// EXCEPTION HANDLER ////////////////////
10921088
if (handleExceptions) {
1089+
// Build a function which maps custom java Exceptions to corresponding native exceptions.
1090+
out.println("static JavaCPP_noinline jclass JavaCPP_mapCustomExceptions(JNIEnv *env, const std::exception& e) {");
1091+
1092+
final String ifCondition = " if(dynamic_cast<const %s*>(&e)){";
1093+
final String elifCondition = " else if(dynamic_cast<const %s*>(&e)){";
1094+
final String returnVal = " return env->FindClass(\"%s\");";
1095+
1096+
List<String> exceptionMappings = clsProperties.get("platform.exceptionMappings");
1097+
for (int i = 0; i < exceptionMappings.size(); i += 2) {
1098+
final String cppException = exceptionMappings.get(i);
1099+
final String javaException = exceptionMappings.get(i + 1);
1100+
1101+
out.println(i == 0 ?
1102+
String.format(ifCondition, cppException) :
1103+
String.format(elifCondition, cppException));
1104+
1105+
out.println(String.format(returnVal, javaException));
1106+
out.println(" }");
1107+
}
1108+
out.println(" return nullptr;");
1109+
out.println("}");
1110+
1111+
// "Import" the remaining exception-specific functions.
10931112
try (InputStream inputStream = Generator.class.getResourceAsStream("/org/bytedeco/javacpp/jniTemplates/JNIExceptionHandler.h");
10941113
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
10951114

@@ -1101,6 +1120,7 @@ boolean classes(boolean handleExceptions, boolean defineAdapters, boolean conver
11011120
throw new RuntimeException("Unable to find template 'JNIExceptionHandler'", e);
11021121
}
11031122
}
1123+
//////////////////// EXCEPTION HANDLER ////////////////////
11041124

11051125
Class deallocator, nativeDeallocator;
11061126
try {

src/main/resources/org/bytedeco/javacpp/jniTemplates/JNIExceptionHandler.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Function finds the right java-exception to the most common native std::exceptions
12
static JavaCPP_noinline jclass JavaCPP_mapJavaExceptions(JNIEnv *env, const std::exception& e){
23
if (dynamic_cast<const std::invalid_argument*>(&e)) {
34
return env->FindClass("java/lang/IllegalArgumentException");
@@ -10,9 +11,11 @@ static JavaCPP_noinline jclass JavaCPP_mapJavaExceptions(JNIEnv *env, const std:
1011
}
1112
return env->FindClass("java/lang/Exception");
1213
}
13-
14+
// Function creates a jobject (extends Throwable) from the passed std::exception
1415
static JavaCPP_noinline jthrowable JavaCPP_createJavaException(JNIEnv *env, const std::exception& e, jthrowable cause = nullptr) {
15-
jclass exClass = JavaCPP_mapJavaExceptions(env, e);
16+
jclass exClass = JavaCPP_mapCustomExceptions(env, e);
17+
exClass = exClass ? exClass : JavaCPP_mapJavaExceptions(env, e);
18+
1619
jstring message = env->NewStringUTF(e.what());
1720
jmethodID constructor = env->GetMethodID(exClass, "<init>", "(Ljava/lang/String;)V");
1821

@@ -24,7 +27,7 @@ static JavaCPP_noinline jthrowable JavaCPP_createJavaException(JNIEnv *env, cons
2427
}
2528
return static_cast<jthrowable>(env->NewObject(exClass, constructor, message));
2629
}
27-
30+
// Function unrolls native exceptions and builds the corresponding java exception
2831
static JavaCPP_noinline jthrowable JavaCPP_handleException(JNIEnv *env, const std::exception& e) {
2932
try {
3033
std::rethrow_if_nested(e);

0 commit comments

Comments
 (0)