Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
D
djinni
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
djinni
Commits
d155107a
Commit
d155107a
authored
Apr 14, 2015
by
Andrew Twyman
Committed by
Jacob Potter
Apr 14, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Framework for JNI exception propagation through C++
parent
28d70153
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
159 additions
and
43 deletions
+159
-43
support-lib/jni/djinni_support.cpp
support-lib/jni/djinni_support.cpp
+45
-12
support-lib/jni/djinni_support.hpp
support-lib/jni/djinni_support.hpp
+114
-31
No files found.
support-lib/jni/djinni_support.cpp
View file @
d155107a
...
@@ -20,6 +20,12 @@
...
@@ -20,6 +20,12 @@
static_assert
(
sizeof
(
jlong
)
>=
sizeof
(
void
*
),
"must be able to fit a void* into a jlong"
);
static_assert
(
sizeof
(
jlong
)
>=
sizeof
(
void
*
),
"must be able to fit a void* into a jlong"
);
#ifdef _MSC_VER // weak attribute not supported by MSVC
#define DJINNI_WEAK_DEFINITION
#else
#define DJINNI_WEAK_DEFINITION __attribute__((weak))
#endif
namespace
djinni
{
namespace
djinni
{
// Set only once from JNI_OnLoad before any other JNI calls, so no lock needed.
// Set only once from JNI_OnLoad before any other JNI calls, so no lock needed.
...
@@ -32,7 +38,11 @@ void jniInit(JavaVM * jvm) {
...
@@ -32,7 +38,11 @@ void jniInit(JavaVM * jvm) {
for
(
const
auto
&
kv
:
JniClassInitializer
::
Registration
::
get_all
())
{
for
(
const
auto
&
kv
:
JniClassInitializer
::
Registration
::
get_all
())
{
kv
.
second
->
init
();
kv
.
second
->
init
();
}
}
}
catch
(
const
jni_exception_pending
&
)
{}
}
catch
(
const
std
::
exception
&
e
)
{
// Default exception handling only, since non-default might not be safe if init
// is incomplete.
jniDefaultSetPendingFromCurrent
(
jniGetThreadEnv
(),
__func__
);
}
}
}
void
jniShutdown
()
{
void
jniShutdown
()
{
...
@@ -89,15 +99,28 @@ void LocalRefDeleter::operator() (jobject localRef) noexcept {
...
@@ -89,15 +99,28 @@ void LocalRefDeleter::operator() (jobject localRef) noexcept {
}
}
}
}
void
jni_exception
::
set_as_pending
(
JNIEnv
*
env
)
const
noexcept
{
assert
(
env
);
env
->
Throw
(
java_exception
());
}
void
jniExceptionCheck
(
JNIEnv
*
env
)
{
void
jniExceptionCheck
(
JNIEnv
*
env
)
{
if
(
!
env
)
{
if
(
!
env
)
{
abort
();
abort
();
}
}
if
(
env
->
ExceptionCheck
())
{
const
LocalRef
<
jthrowable
>
e
(
env
->
ExceptionOccurred
());
throw
jni_exception_pending
();
if
(
e
)
{
env
->
ExceptionClear
();
jniThrowCppFromJavaException
(
env
,
e
.
get
());
}
}
}
}
DJINNI_WEAK_DEFINITION
__attribute__
((
noreturn
))
void
jniThrowCppFromJavaException
(
JNIEnv
*
env
,
jthrowable
java_exception
)
{
throw
jni_exception
{
env
,
java_exception
};
}
namespace
{
// anonymous namespace to guard the struct below
namespace
{
// anonymous namespace to guard the struct below
struct
SystemClassInfo
{
struct
SystemClassInfo
{
// This is a singleton class - an instance will be constructed by
// This is a singleton class - an instance will be constructed by
...
@@ -141,13 +164,18 @@ void jniThrowAssertionError(JNIEnv * env, const char * file, int line, const cha
...
@@ -141,13 +164,18 @@ void jniThrowAssertionError(JNIEnv * env, const char * file, int line, const cha
snprintf
(
buf
,
sizeof
buf
,
"djinni (%s:%d): %s"
,
file_basename
,
line
,
check
);
snprintf
(
buf
,
sizeof
buf
,
"djinni (%s:%d): %s"
,
file_basename
,
line
,
check
);
#endif
#endif
jclass
cassert
=
env
->
FindClass
(
"java/lang/Assertion
Error"
);
const
jclass
cassert
=
env
->
FindClass
(
"java/lang/
Error"
);
assert
(
cassert
);
assert
(
cassert
);
env
->
ThrowNew
(
cassert
,
buf
);
env
->
ThrowNew
(
cassert
,
buf
);
assert
(
env
->
ExceptionCheck
());
assert
(
env
->
ExceptionCheck
());
const
jthrowable
e
=
env
->
ExceptionOccurred
();
assert
(
e
);
env
->
ExceptionClear
();
env
->
DeleteLocalRef
(
cassert
);
env
->
DeleteLocalRef
(
cassert
);
throw
jni_exception_pending
{};
env
->
DeleteLocalRef
(
e
);
jniThrowCppFromJavaException
(
env
,
e
);
}
}
GlobalRef
<
jclass
>
jniFindClass
(
const
char
*
name
)
{
GlobalRef
<
jclass
>
jniFindClass
(
const
char
*
name
)
{
...
@@ -416,19 +444,24 @@ std::string jniUTF8FromString(JNIEnv * env, const jstring jstr) {
...
@@ -416,19 +444,24 @@ std::string jniUTF8FromString(JNIEnv * env, const jstring jstr) {
return
out
;
return
out
;
}
}
#ifdef _MSC_VER
DJINNI_WEAK_DEFINITION
// weak attribute not supported by MSVC
void
jniSetPendingFromCurrent
(
JNIEnv
*
env
,
const
char
*
ctx
)
noexcept
{
#else
jniDefaultSetPendingFromCurrent
(
env
,
ctx
);
__attribute__
((
weak
))
}
#endif
void
jniSetPendingFromCurrent
(
JNIEnv
*
env
,
const
char
*
/*ctx*/
)
noexcept
{
void
jniDefaultSetPendingFromCurrent
(
JNIEnv
*
env
,
const
char
*
/*ctx*/
)
noexcept
{
assert
(
env
);
try
{
try
{
throw
;
throw
;
}
catch
(
const
jni_exception_pending
&
)
{
}
catch
(
const
jni_exception
&
e
)
{
e
.
set_as_pending
(
env
);
return
;
return
;
}
catch
(
const
std
::
exception
&
e
)
{
}
catch
(
const
std
::
exception
&
e
)
{
env
->
ThrowNew
(
env
->
FindClass
(
"java/lang/RuntimeException"
),
e
.
what
());
env
->
ThrowNew
(
env
->
FindClass
(
"java/lang/RuntimeException"
),
e
.
what
());
}
}
// noexcept will call terminate() for anything not caught above (i.e.
// exceptions which aren't std::exception subclasses).
}
}
struct
JavaProxyCacheState
{
struct
JavaProxyCacheState
{
...
...
support-lib/jni/djinni_support.hpp
View file @
d155107a
...
@@ -51,34 +51,6 @@ void jniShutdown();
...
@@ -51,34 +51,6 @@ void jniShutdown();
*/
*/
JNIEnv
*
jniGetThreadEnv
();
JNIEnv
*
jniGetThreadEnv
();
/*
* Exception to indicate that a Java exception is pending in the JVM.
*/
class
jni_exception_pending
:
public
std
::
exception
{};
/*
* Throw jni_exception_pending if any Java exception is pending in the JVM.
*/
void
jniExceptionCheck
(
JNIEnv
*
env
);
/*
* Set an AssertionError in env with message message, and then throw jni_exception_pending.
*/
#ifdef _MSC_VER
__declspec
(
noreturn
)
#else
__attribute__
((
noreturn
))
#endif
void
jniThrowAssertionError
(
JNIEnv
*
env
,
const
char
*
file
,
int
line
,
const
char
*
check
);
#define DJINNI_ASSERT(check, env) \
do { \
djinni::jniExceptionCheck(env); \
const bool check__res = bool(check); \
djinni::jniExceptionCheck(env); \
if (!check__res) { \
djinni::jniThrowAssertionError(env, __FILE__, __LINE__, #check); \
} \
} while(false)
/*
/*
* Global and local reference guard objects.
* Global and local reference guard objects.
*
*
...
@@ -122,7 +94,69 @@ public:
...
@@ -122,7 +94,69 @@ public:
};
};
/*
/*
* Helper for JniClassInitializer. Copied from Oxygen.
* Exception to indicate that a Java exception is pending in the JVM.
*/
class
jni_exception
:
public
std
::
exception
{
GlobalRef
<
jthrowable
>
m_java_exception
;
public:
jni_exception
(
JNIEnv
*
env
,
jthrowable
java_exception
)
:
m_java_exception
(
env
,
java_exception
)
{
assert
(
java_exception
);
}
jthrowable
java_exception
()
const
{
return
m_java_exception
.
get
();
}
/*
* Sets the pending JNI exception using this Java exception.
*/
void
set_as_pending
(
JNIEnv
*
env
)
const
noexcept
;
};
/*
* Throw if any Java exception is pending in the JVM.
*
* If an exception is pending, this function will clear the
* pending state, and pass the exception to
* jniThrowCppFromJavaException().
*/
void
jniExceptionCheck
(
JNIEnv
*
env
);
/*
* Throws a C++ exception based on the given Java exception.
*
* java_exception is a local reference to a Java throwable, which
* must not be null, and should no longer set as "pending" in the JVM.
* This is called to handle errors in other JNI processing, including
* by jniExceptionCheck().
*
* The default implementation is defined with __attribute__((weak)) so you
* can replace it by defining your own version. The default implementation
* will throw a jni_exception containing the given jthrowable.
*/
__attribute__
((
noreturn
))
void
jniThrowCppFromJavaException
(
JNIEnv
*
env
,
jthrowable
java_exception
);
/*
* Set an AssertionError in env with message message, and then throw via jniExceptionCheck.
*/
#ifdef _MSC_VER
__declspec
(
noreturn
)
#else
__attribute__
((
noreturn
))
#endif
void
jniThrowAssertionError
(
JNIEnv
*
env
,
const
char
*
file
,
int
line
,
const
char
*
check
);
#define DJINNI_ASSERT(check, env) \
do { \
djinni::jniExceptionCheck(env); \
const bool check__res = bool(check); \
djinni::jniExceptionCheck(env); \
if (!check__res) { \
djinni::jniThrowAssertionError(env, __FILE__, __LINE__, #check); \
} \
} while(false)
/*
* Helper for JniClassInitializer.
*/
*/
template
<
class
Key
,
class
T
>
template
<
class
Key
,
class
T
>
class
static_registration
{
class
static_registration
{
...
@@ -377,7 +411,11 @@ public:
...
@@ -377,7 +411,11 @@ public:
}
}
static
const
std
::
shared_ptr
<
T
>
&
get
(
jlong
handle
)
{
static
const
std
::
shared_ptr
<
T
>
&
get
(
jlong
handle
)
{
return
reinterpret_cast
<
const
CppProxyHandle
<
T
>
*>
(
handle
)
->
m_obj
;
assert
(
handle
);
assert
(
handle
>
4096
);
const
auto
&
ret
=
reinterpret_cast
<
const
CppProxyHandle
<
T
>
*>
(
handle
)
->
m_obj
;
assert
(
ret
);
return
ret
;
}
}
private:
private:
...
@@ -577,9 +615,35 @@ private:
...
@@ -577,9 +615,35 @@ private:
#define DJINNI_FUNCTION_PROLOGUE0(env_)
#define DJINNI_FUNCTION_PROLOGUE0(env_)
#define DJINNI_FUNCTION_PROLOGUE1(env_, arg1_)
#define DJINNI_FUNCTION_PROLOGUE1(env_, arg1_)
// Helper for JNI_TRANSLATE_EXCEPTIONS_RETURN. Do not call directly.
/*
* Helper for JNI_TRANSLATE_EXCEPTIONS_RETURN.
*
* Must be called in a catch block. Responsible for setting the pending
* exception in JNI based on the current C++ exception.
*
* The default implementation is defined with __attribute__((weak)) so you
* can replace it by defining your own version. The default implementation
* will call jniDefaultSetPendingFromCurrent(), which will propagate a
* jni_exception directly into Java, or throw a RuntimeException for any
* other std::exception.
*/
void
jniSetPendingFromCurrent
(
JNIEnv
*
env
,
const
char
*
ctx
)
noexcept
;
void
jniSetPendingFromCurrent
(
JNIEnv
*
env
,
const
char
*
ctx
)
noexcept
;
/*
* Helper for JNI_TRANSLATE_EXCEPTIONS_RETURN.
*
* Must be called in a catch block. Responsible for setting the pending
* exception in JNI based on the current C++ exception.
*
* This will call jniSetPendingFrom(env, jni_exception) if the current exception
* is a jni_exception, or otherwise will set a RuntimeException from any
* other std::exception. Any non-std::exception will result in a call
* to terminate().
*
* This is called by the default implementation of jniSetPendingFromCurrent.
*/
void
jniDefaultSetPendingFromCurrent
(
JNIEnv
*
env
,
const
char
*
ctx
)
noexcept
;
/* Catch C++ exceptions and translate them to Java exceptions.
/* Catch C++ exceptions and translate them to Java exceptions.
*
*
* All functions called by Java must be fully wrapped by an outer try...catch block like so:
* All functions called by Java must be fully wrapped by an outer try...catch block like so:
...
@@ -599,4 +663,23 @@ void jniSetPendingFromCurrent(JNIEnv * env, const char * ctx) noexcept;
...
@@ -599,4 +663,23 @@ void jniSetPendingFromCurrent(JNIEnv * env, const char * ctx) noexcept;
return ret; \
return ret; \
}
}
/* Catch jni_exception and translate it back to a Java exception, without catching
* any other C++ exceptions. Can be used to wrap code which might cause JNI
* exceptions like so:
*
* try {
* ...
* } JNI_TRANSLATE_JAVA_EXCEPTIONS_RETURN(env, 0)
* ... or JNI_TRANSLATE_JAVA_EXCEPTIONS_RETURN(env, ) for functions returning void
*
* The second parameter is a default return value to be used if an exception is caught and
* converted. (For JNI outer-layer calls, this result will always be ignored by JNI, so
* it can safely be 0 for any function with a non-void return value.)
*/
#define JNI_TRANSLATE_JNI_EXCEPTIONS_RETURN(env, ret) \
catch (const ::djinni::jni_exception & e) { \
e.set_as_pending(env); \
return ret; \
}
}
// namespace djinni
}
// namespace djinni
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment