123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- /* -----------------------------------------------------------------------------
- * director.swg
- *
- * This file contains support for director classes so that Java proxy
- * methods can be called from C++.
- * ----------------------------------------------------------------------------- */
- #if defined(DEBUG_DIRECTOR_OWNED) || defined(DEBUG_DIRECTOR_EXCEPTION)
- #include <iostream>
- #endif
- #include <exception>
- namespace Swig {
- /* Java object wrapper */
- class JObjectWrapper {
- public:
- JObjectWrapper() : jthis_(NULL), weak_global_(true) {
- }
- ~JObjectWrapper() {
- jthis_ = NULL;
- weak_global_ = true;
- }
- bool set(JNIEnv *jenv, jobject jobj, bool mem_own, bool weak_global) {
- if (!jthis_) {
- weak_global_ = weak_global || !mem_own; // hold as weak global if explicitly requested or not owned
- if (jobj)
- jthis_ = weak_global_ ? jenv->NewWeakGlobalRef(jobj) : jenv->NewGlobalRef(jobj);
- #if defined(DEBUG_DIRECTOR_OWNED)
- std::cout << "JObjectWrapper::set(" << jobj << ", " << (weak_global ? "weak_global" : "global_ref") << ") -> " << jthis_ << std::endl;
- #endif
- return true;
- } else {
- #if defined(DEBUG_DIRECTOR_OWNED)
- std::cout << "JObjectWrapper::set(" << jobj << ", " << (weak_global ? "weak_global" : "global_ref") << ") -> already set" << std::endl;
- #endif
- return false;
- }
- }
- jobject get(JNIEnv *jenv) const {
- #if defined(DEBUG_DIRECTOR_OWNED)
- std::cout << "JObjectWrapper::get(";
- if (jthis_)
- std::cout << jthis_;
- else
- std::cout << "null";
- std::cout << ") -> return new local ref" << std::endl;
- #endif
- return (jthis_ ? jenv->NewLocalRef(jthis_) : jthis_);
- }
- void release(JNIEnv *jenv) {
- #if defined(DEBUG_DIRECTOR_OWNED)
- std::cout << "JObjectWrapper::release(" << jthis_ << "): " << (weak_global_ ? "weak global ref" : "global ref") << std::endl;
- #endif
- if (jthis_) {
- if (weak_global_) {
- if (jenv->IsSameObject(jthis_, NULL) == JNI_FALSE)
- jenv->DeleteWeakGlobalRef((jweak)jthis_);
- } else
- jenv->DeleteGlobalRef(jthis_);
- }
- jthis_ = NULL;
- weak_global_ = true;
- }
- /* Only call peek if you know what you are doing wrt to weak/global references */
- jobject peek() {
- return jthis_;
- }
- /* Java proxy releases ownership of C++ object, C++ object is now
- responsible for destruction (creates NewGlobalRef to pin Java proxy) */
- void java_change_ownership(JNIEnv *jenv, jobject jself, bool take_or_release) {
- if (take_or_release) { /* Java takes ownership of C++ object's lifetime. */
- if (!weak_global_) {
- jenv->DeleteGlobalRef(jthis_);
- jthis_ = jenv->NewWeakGlobalRef(jself);
- weak_global_ = true;
- }
- } else {
- /* Java releases ownership of C++ object's lifetime */
- if (weak_global_) {
- jenv->DeleteWeakGlobalRef((jweak)jthis_);
- jthis_ = jenv->NewGlobalRef(jself);
- weak_global_ = false;
- }
- }
- }
- private:
- /* pointer to Java object */
- jobject jthis_;
- /* Local or global reference flag */
- bool weak_global_;
- };
- /* Local JNI reference deleter */
- class LocalRefGuard {
- JNIEnv *jenv_;
- jobject jobj_;
- // non-copyable
- LocalRefGuard(const LocalRefGuard &);
- LocalRefGuard &operator=(const LocalRefGuard &);
- public:
- LocalRefGuard(JNIEnv *jenv, jobject jobj): jenv_(jenv), jobj_(jobj) {}
- ~LocalRefGuard() {
- if (jobj_)
- jenv_->DeleteLocalRef(jobj_);
- }
- };
- /* director base class */
- class Director {
- /* pointer to Java virtual machine */
- JavaVM *swig_jvm_;
- protected:
- #if defined (_MSC_VER) && (_MSC_VER<1300)
- class JNIEnvWrapper;
- friend class JNIEnvWrapper;
- #endif
- /* Utility class for managing the JNI environment */
- class JNIEnvWrapper {
- const Director *director_;
- JNIEnv *jenv_;
- int env_status;
- public:
- JNIEnvWrapper(const Director *director) : director_(director), jenv_(0), env_status(0) {
- #if defined(__ANDROID__)
- JNIEnv **jenv = &jenv_;
- #else
- void **jenv = (void **)&jenv_;
- #endif
- env_status = director_->swig_jvm_->GetEnv((void **)&jenv_, JNI_VERSION_1_2);
- #if defined(SWIG_JAVA_ATTACH_CURRENT_THREAD_AS_DAEMON)
- // Attach a daemon thread to the JVM. Useful when the JVM should not wait for
- // the thread to exit upon shutdown. Only for jdk-1.4 and later.
- director_->swig_jvm_->AttachCurrentThreadAsDaemon(jenv, NULL);
- #else
- director_->swig_jvm_->AttachCurrentThread(jenv, NULL);
- #endif
- }
- ~JNIEnvWrapper() {
- #if !defined(SWIG_JAVA_NO_DETACH_CURRENT_THREAD)
- // Some JVMs, eg jdk-1.4.2 and lower on Solaris have a bug and crash with the DetachCurrentThread call.
- // However, without this call, the JVM hangs on exit when the thread was not created by the JVM and creates a memory leak.
- if (env_status == JNI_EDETACHED)
- director_->swig_jvm_->DetachCurrentThread();
- #endif
- }
- JNIEnv *getJNIEnv() const {
- return jenv_;
- }
- };
- /* Java object wrapper */
- JObjectWrapper swig_self_;
- /* Disconnect director from Java object */
- void swig_disconnect_director_self(const char *disconn_method) {
- JNIEnvWrapper jnienv(this) ;
- JNIEnv *jenv = jnienv.getJNIEnv() ;
- jobject jobj = swig_self_.get(jenv);
- LocalRefGuard ref_deleter(jenv, jobj);
- #if defined(DEBUG_DIRECTOR_OWNED)
- std::cout << "Swig::Director::disconnect_director_self(" << jobj << ")" << std::endl;
- #endif
- if (jobj && jenv->IsSameObject(jobj, NULL) == JNI_FALSE) {
- jmethodID disconn_meth = jenv->GetMethodID(jenv->GetObjectClass(jobj), disconn_method, "()V");
- if (disconn_meth) {
- #if defined(DEBUG_DIRECTOR_OWNED)
- std::cout << "Swig::Director::disconnect_director_self upcall to " << disconn_method << std::endl;
- #endif
- jenv->CallVoidMethod(jobj, disconn_meth);
- }
- }
- }
- public:
- Director(JNIEnv *jenv) : swig_jvm_((JavaVM *) NULL), swig_self_() {
- /* Acquire the Java VM pointer */
- jenv->GetJavaVM(&swig_jvm_);
- }
- virtual ~Director() {
- JNIEnvWrapper jnienv(this) ;
- JNIEnv *jenv = jnienv.getJNIEnv() ;
- swig_self_.release(jenv);
- }
- bool swig_set_self(JNIEnv *jenv, jobject jself, bool mem_own, bool weak_global) {
- return swig_self_.set(jenv, jself, mem_own, weak_global);
- }
- jobject swig_get_self(JNIEnv *jenv) const {
- return swig_self_.get(jenv);
- }
- // Change C++ object's ownership, relative to Java
- void swig_java_change_ownership(JNIEnv *jenv, jobject jself, bool take_or_release) {
- swig_self_.java_change_ownership(jenv, jself, take_or_release);
- }
- };
- // Zero initialized bool array
- template<size_t N> class BoolArray {
- bool array_[N];
- public:
- BoolArray() {
- memset(array_, 0, sizeof(array_));
- }
- bool& operator[](size_t n) {
- return array_[n];
- }
- bool operator[](size_t n) const {
- return array_[n];
- }
- };
- // Utility classes and functions for exception handling.
- // Simple holder for a Java string during exception handling, providing access to a c-style string
- class JavaString {
- public:
- JavaString(JNIEnv *jenv, jstring jstr) : jenv_(jenv), jstr_(jstr), cstr_(0) {
- if (jenv_ && jstr_)
- cstr_ = (const char *) jenv_->GetStringUTFChars(jstr_, NULL);
- }
- ~JavaString() {
- if (jenv_ && jstr_ && cstr_)
- jenv_->ReleaseStringUTFChars(jstr_, cstr_);
- }
- const char *c_str(const char *null_string = "null JavaString") const {
- return cstr_ ? cstr_ : null_string;
- }
- private:
- // non-copyable
- JavaString(const JavaString &);
- JavaString &operator=(const JavaString &);
- JNIEnv *jenv_;
- jstring jstr_;
- const char *cstr_;
- };
- // Helper class to extract the exception message from a Java throwable
- class JavaExceptionMessage {
- public:
- JavaExceptionMessage(JNIEnv *jenv, jthrowable throwable) : message_(jenv, exceptionMessageFromThrowable(jenv, throwable)) {
- }
- const char *message() const {
- return message_.c_str("Could not get exception message in JavaExceptionMessage");
- }
- private:
- // non-copyable
- JavaExceptionMessage(const JavaExceptionMessage &);
- JavaExceptionMessage &operator=(const JavaExceptionMessage &);
- // Get exception message by calling Java method Throwable.getMessage()
- static jstring exceptionMessageFromThrowable(JNIEnv *jenv, jthrowable throwable) {
- jstring jmsg = NULL;
- if (jenv && throwable) {
- jenv->ExceptionClear(); // Cannot invoke methods with any pending exceptions
- jclass throwclz = jenv->GetObjectClass(throwable);
- if (throwclz) {
- // All Throwable classes have a getMessage() method, so call it to extract the exception message
- jmethodID getMessageMethodID = jenv->GetMethodID(throwclz, "getMessage", "()Ljava/lang/String;");
- if (getMessageMethodID)
- jmsg = (jstring)jenv->CallObjectMethod(throwable, getMessageMethodID);
- }
- if (jmsg == NULL && jenv->ExceptionCheck())
- jenv->ExceptionClear();
- }
- return jmsg;
- }
- JavaString message_;
- };
- // C++ Exception class for handling Java exceptions thrown during a director method Java upcall
- class DirectorException : public std::exception {
- public:
- // Construct exception from a Java throwable
- DirectorException(JNIEnv *jenv, jthrowable throwable) : classname_(0), msg_(0) {
- // Call Java method Object.getClass().getName() to obtain the throwable's class name (delimited by '/')
- if (throwable) {
- jclass throwclz = jenv->GetObjectClass(throwable);
- if (throwclz) {
- jclass clzclz = jenv->GetObjectClass(throwclz);
- if (clzclz) {
- jmethodID getNameMethodID = jenv->GetMethodID(clzclz, "getName", "()Ljava/lang/String;");
- if (getNameMethodID) {
- jstring jstr_classname = (jstring)(jenv->CallObjectMethod(throwclz, getNameMethodID));
- // Copy strings, since there is no guarantee that jenv will be active when handled
- if (jstr_classname) {
- JavaString jsclassname(jenv, jstr_classname);
- const char *classname = jsclassname.c_str(0);
- if (classname)
- classname_ = copypath(classname);
- }
- }
- }
- }
- }
- JavaExceptionMessage exceptionmsg(jenv, throwable);
- msg_ = copystr(exceptionmsg.message());
- }
- // More general constructor for handling as a java.lang.RuntimeException
- DirectorException(const char *msg) : classname_(0), msg_(copystr(msg ? msg : "Unspecified DirectorException message")) {
- }
- ~DirectorException() throw() {
- delete[] classname_;
- delete[] msg_;
- }
- const char *what() const throw() {
- return msg_;
- }
- // Reconstruct and raise/throw the Java Exception that caused the DirectorException
- // Note that any error in the JNI exception handling results in a Java RuntimeException
- void raiseJavaException(JNIEnv *jenv) const {
- if (jenv) {
- jenv->ExceptionClear();
- jmethodID ctorMethodID = 0;
- jclass throwableclass = 0;
- if (classname_) {
- throwableclass = jenv->FindClass(classname_);
- if (throwableclass)
- ctorMethodID = jenv->GetMethodID(throwableclass, "<init>", "(Ljava/lang/String;)V");
- }
- if (ctorMethodID) {
- jenv->ThrowNew(throwableclass, what());
- } else {
- SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, what());
- }
- }
- }
- private:
- static char *copypath(const char *srcmsg) {
- char *target = copystr(srcmsg);
- for (char *c=target; *c; ++c) {
- if ('.' == *c)
- *c = '/';
- }
- return target;
- }
- static char *copystr(const char *srcmsg) {
- char *target = 0;
- if (srcmsg) {
- size_t msglen = strlen(srcmsg) + 1;
- target = new char[msglen];
- strncpy(target, srcmsg, msglen);
- }
- return target;
- }
- const char *classname_;
- const char *msg_;
- };
- // Helper method to determine if a Java throwable matches a particular Java class type
- SWIGINTERN bool ExceptionMatches(JNIEnv *jenv, jthrowable throwable, const char *classname) {
- bool matches = false;
- if (throwable && jenv && classname) {
- // Exceptions need to be cleared for correct behavior.
- // The caller of ExceptionMatches should restore pending exceptions if desired -
- // the caller already has the throwable.
- jenv->ExceptionClear();
- jclass clz = jenv->FindClass(classname);
- if (clz) {
- jclass classclz = jenv->GetObjectClass(clz);
- jmethodID isInstanceMethodID = jenv->GetMethodID(classclz, "isInstance", "(Ljava/lang/Object;)Z");
- if (isInstanceMethodID) {
- matches = jenv->CallBooleanMethod(clz, isInstanceMethodID, throwable) != 0;
- }
- }
- #if defined(DEBUG_DIRECTOR_EXCEPTION)
- if (jenv->ExceptionCheck()) {
- // Typically occurs when an invalid classname argument is passed resulting in a ClassNotFoundException
- JavaExceptionMessage exc(jenv, jenv->ExceptionOccurred());
- std::cout << "Error: ExceptionMatches: class '" << classname << "' : " << exc.message() << std::endl;
- }
- #endif
- }
- return matches;
- }
- }
|