director.swg 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. /* -----------------------------------------------------------------------------
  2. * director.swg
  3. *
  4. * This file contains support for director classes so that Java proxy
  5. * methods can be called from C++.
  6. * ----------------------------------------------------------------------------- */
  7. #if defined(DEBUG_DIRECTOR_OWNED) || defined(DEBUG_DIRECTOR_EXCEPTION)
  8. #include <iostream>
  9. #endif
  10. #include <exception>
  11. namespace Swig {
  12. /* Java object wrapper */
  13. class JObjectWrapper {
  14. public:
  15. JObjectWrapper() : jthis_(NULL), weak_global_(true) {
  16. }
  17. ~JObjectWrapper() {
  18. jthis_ = NULL;
  19. weak_global_ = true;
  20. }
  21. bool set(JNIEnv *jenv, jobject jobj, bool mem_own, bool weak_global) {
  22. if (!jthis_) {
  23. weak_global_ = weak_global || !mem_own; // hold as weak global if explicitly requested or not owned
  24. if (jobj)
  25. jthis_ = weak_global_ ? jenv->NewWeakGlobalRef(jobj) : jenv->NewGlobalRef(jobj);
  26. #if defined(DEBUG_DIRECTOR_OWNED)
  27. std::cout << "JObjectWrapper::set(" << jobj << ", " << (weak_global ? "weak_global" : "global_ref") << ") -> " << jthis_ << std::endl;
  28. #endif
  29. return true;
  30. } else {
  31. #if defined(DEBUG_DIRECTOR_OWNED)
  32. std::cout << "JObjectWrapper::set(" << jobj << ", " << (weak_global ? "weak_global" : "global_ref") << ") -> already set" << std::endl;
  33. #endif
  34. return false;
  35. }
  36. }
  37. jobject get(JNIEnv *jenv) const {
  38. #if defined(DEBUG_DIRECTOR_OWNED)
  39. std::cout << "JObjectWrapper::get(";
  40. if (jthis_)
  41. std::cout << jthis_;
  42. else
  43. std::cout << "null";
  44. std::cout << ") -> return new local ref" << std::endl;
  45. #endif
  46. return (jthis_ ? jenv->NewLocalRef(jthis_) : jthis_);
  47. }
  48. void release(JNIEnv *jenv) {
  49. #if defined(DEBUG_DIRECTOR_OWNED)
  50. std::cout << "JObjectWrapper::release(" << jthis_ << "): " << (weak_global_ ? "weak global ref" : "global ref") << std::endl;
  51. #endif
  52. if (jthis_) {
  53. if (weak_global_) {
  54. if (jenv->IsSameObject(jthis_, NULL) == JNI_FALSE)
  55. jenv->DeleteWeakGlobalRef((jweak)jthis_);
  56. } else
  57. jenv->DeleteGlobalRef(jthis_);
  58. }
  59. jthis_ = NULL;
  60. weak_global_ = true;
  61. }
  62. /* Only call peek if you know what you are doing wrt to weak/global references */
  63. jobject peek() {
  64. return jthis_;
  65. }
  66. /* Java proxy releases ownership of C++ object, C++ object is now
  67. responsible for destruction (creates NewGlobalRef to pin Java proxy) */
  68. void java_change_ownership(JNIEnv *jenv, jobject jself, bool take_or_release) {
  69. if (take_or_release) { /* Java takes ownership of C++ object's lifetime. */
  70. if (!weak_global_) {
  71. jenv->DeleteGlobalRef(jthis_);
  72. jthis_ = jenv->NewWeakGlobalRef(jself);
  73. weak_global_ = true;
  74. }
  75. } else {
  76. /* Java releases ownership of C++ object's lifetime */
  77. if (weak_global_) {
  78. jenv->DeleteWeakGlobalRef((jweak)jthis_);
  79. jthis_ = jenv->NewGlobalRef(jself);
  80. weak_global_ = false;
  81. }
  82. }
  83. }
  84. private:
  85. /* pointer to Java object */
  86. jobject jthis_;
  87. /* Local or global reference flag */
  88. bool weak_global_;
  89. };
  90. /* Local JNI reference deleter */
  91. class LocalRefGuard {
  92. JNIEnv *jenv_;
  93. jobject jobj_;
  94. // non-copyable
  95. LocalRefGuard(const LocalRefGuard &);
  96. LocalRefGuard &operator=(const LocalRefGuard &);
  97. public:
  98. LocalRefGuard(JNIEnv *jenv, jobject jobj): jenv_(jenv), jobj_(jobj) {}
  99. ~LocalRefGuard() {
  100. if (jobj_)
  101. jenv_->DeleteLocalRef(jobj_);
  102. }
  103. };
  104. /* director base class */
  105. class Director {
  106. /* pointer to Java virtual machine */
  107. JavaVM *swig_jvm_;
  108. protected:
  109. #if defined (_MSC_VER) && (_MSC_VER<1300)
  110. class JNIEnvWrapper;
  111. friend class JNIEnvWrapper;
  112. #endif
  113. /* Utility class for managing the JNI environment */
  114. class JNIEnvWrapper {
  115. const Director *director_;
  116. JNIEnv *jenv_;
  117. int env_status;
  118. public:
  119. JNIEnvWrapper(const Director *director) : director_(director), jenv_(0), env_status(0) {
  120. #if defined(__ANDROID__)
  121. JNIEnv **jenv = &jenv_;
  122. #else
  123. void **jenv = (void **)&jenv_;
  124. #endif
  125. env_status = director_->swig_jvm_->GetEnv((void **)&jenv_, JNI_VERSION_1_2);
  126. #if defined(SWIG_JAVA_ATTACH_CURRENT_THREAD_AS_DAEMON)
  127. // Attach a daemon thread to the JVM. Useful when the JVM should not wait for
  128. // the thread to exit upon shutdown. Only for jdk-1.4 and later.
  129. director_->swig_jvm_->AttachCurrentThreadAsDaemon(jenv, NULL);
  130. #else
  131. director_->swig_jvm_->AttachCurrentThread(jenv, NULL);
  132. #endif
  133. }
  134. ~JNIEnvWrapper() {
  135. #if !defined(SWIG_JAVA_NO_DETACH_CURRENT_THREAD)
  136. // Some JVMs, eg jdk-1.4.2 and lower on Solaris have a bug and crash with the DetachCurrentThread call.
  137. // However, without this call, the JVM hangs on exit when the thread was not created by the JVM and creates a memory leak.
  138. if (env_status == JNI_EDETACHED)
  139. director_->swig_jvm_->DetachCurrentThread();
  140. #endif
  141. }
  142. JNIEnv *getJNIEnv() const {
  143. return jenv_;
  144. }
  145. };
  146. /* Java object wrapper */
  147. JObjectWrapper swig_self_;
  148. /* Disconnect director from Java object */
  149. void swig_disconnect_director_self(const char *disconn_method) {
  150. JNIEnvWrapper jnienv(this) ;
  151. JNIEnv *jenv = jnienv.getJNIEnv() ;
  152. jobject jobj = swig_self_.get(jenv);
  153. LocalRefGuard ref_deleter(jenv, jobj);
  154. #if defined(DEBUG_DIRECTOR_OWNED)
  155. std::cout << "Swig::Director::disconnect_director_self(" << jobj << ")" << std::endl;
  156. #endif
  157. if (jobj && jenv->IsSameObject(jobj, NULL) == JNI_FALSE) {
  158. jmethodID disconn_meth = jenv->GetMethodID(jenv->GetObjectClass(jobj), disconn_method, "()V");
  159. if (disconn_meth) {
  160. #if defined(DEBUG_DIRECTOR_OWNED)
  161. std::cout << "Swig::Director::disconnect_director_self upcall to " << disconn_method << std::endl;
  162. #endif
  163. jenv->CallVoidMethod(jobj, disconn_meth);
  164. }
  165. }
  166. }
  167. public:
  168. Director(JNIEnv *jenv) : swig_jvm_((JavaVM *) NULL), swig_self_() {
  169. /* Acquire the Java VM pointer */
  170. jenv->GetJavaVM(&swig_jvm_);
  171. }
  172. virtual ~Director() {
  173. JNIEnvWrapper jnienv(this) ;
  174. JNIEnv *jenv = jnienv.getJNIEnv() ;
  175. swig_self_.release(jenv);
  176. }
  177. bool swig_set_self(JNIEnv *jenv, jobject jself, bool mem_own, bool weak_global) {
  178. return swig_self_.set(jenv, jself, mem_own, weak_global);
  179. }
  180. jobject swig_get_self(JNIEnv *jenv) const {
  181. return swig_self_.get(jenv);
  182. }
  183. // Change C++ object's ownership, relative to Java
  184. void swig_java_change_ownership(JNIEnv *jenv, jobject jself, bool take_or_release) {
  185. swig_self_.java_change_ownership(jenv, jself, take_or_release);
  186. }
  187. };
  188. // Zero initialized bool array
  189. template<size_t N> class BoolArray {
  190. bool array_[N];
  191. public:
  192. BoolArray() {
  193. memset(array_, 0, sizeof(array_));
  194. }
  195. bool& operator[](size_t n) {
  196. return array_[n];
  197. }
  198. bool operator[](size_t n) const {
  199. return array_[n];
  200. }
  201. };
  202. // Utility classes and functions for exception handling.
  203. // Simple holder for a Java string during exception handling, providing access to a c-style string
  204. class JavaString {
  205. public:
  206. JavaString(JNIEnv *jenv, jstring jstr) : jenv_(jenv), jstr_(jstr), cstr_(0) {
  207. if (jenv_ && jstr_)
  208. cstr_ = (const char *) jenv_->GetStringUTFChars(jstr_, NULL);
  209. }
  210. ~JavaString() {
  211. if (jenv_ && jstr_ && cstr_)
  212. jenv_->ReleaseStringUTFChars(jstr_, cstr_);
  213. }
  214. const char *c_str(const char *null_string = "null JavaString") const {
  215. return cstr_ ? cstr_ : null_string;
  216. }
  217. private:
  218. // non-copyable
  219. JavaString(const JavaString &);
  220. JavaString &operator=(const JavaString &);
  221. JNIEnv *jenv_;
  222. jstring jstr_;
  223. const char *cstr_;
  224. };
  225. // Helper class to extract the exception message from a Java throwable
  226. class JavaExceptionMessage {
  227. public:
  228. JavaExceptionMessage(JNIEnv *jenv, jthrowable throwable) : message_(jenv, exceptionMessageFromThrowable(jenv, throwable)) {
  229. }
  230. const char *message() const {
  231. return message_.c_str("Could not get exception message in JavaExceptionMessage");
  232. }
  233. private:
  234. // non-copyable
  235. JavaExceptionMessage(const JavaExceptionMessage &);
  236. JavaExceptionMessage &operator=(const JavaExceptionMessage &);
  237. // Get exception message by calling Java method Throwable.getMessage()
  238. static jstring exceptionMessageFromThrowable(JNIEnv *jenv, jthrowable throwable) {
  239. jstring jmsg = NULL;
  240. if (jenv && throwable) {
  241. jenv->ExceptionClear(); // Cannot invoke methods with any pending exceptions
  242. jclass throwclz = jenv->GetObjectClass(throwable);
  243. if (throwclz) {
  244. // All Throwable classes have a getMessage() method, so call it to extract the exception message
  245. jmethodID getMessageMethodID = jenv->GetMethodID(throwclz, "getMessage", "()Ljava/lang/String;");
  246. if (getMessageMethodID)
  247. jmsg = (jstring)jenv->CallObjectMethod(throwable, getMessageMethodID);
  248. }
  249. if (jmsg == NULL && jenv->ExceptionCheck())
  250. jenv->ExceptionClear();
  251. }
  252. return jmsg;
  253. }
  254. JavaString message_;
  255. };
  256. // C++ Exception class for handling Java exceptions thrown during a director method Java upcall
  257. class DirectorException : public std::exception {
  258. public:
  259. // Construct exception from a Java throwable
  260. DirectorException(JNIEnv *jenv, jthrowable throwable) : classname_(0), msg_(0) {
  261. // Call Java method Object.getClass().getName() to obtain the throwable's class name (delimited by '/')
  262. if (throwable) {
  263. jclass throwclz = jenv->GetObjectClass(throwable);
  264. if (throwclz) {
  265. jclass clzclz = jenv->GetObjectClass(throwclz);
  266. if (clzclz) {
  267. jmethodID getNameMethodID = jenv->GetMethodID(clzclz, "getName", "()Ljava/lang/String;");
  268. if (getNameMethodID) {
  269. jstring jstr_classname = (jstring)(jenv->CallObjectMethod(throwclz, getNameMethodID));
  270. // Copy strings, since there is no guarantee that jenv will be active when handled
  271. if (jstr_classname) {
  272. JavaString jsclassname(jenv, jstr_classname);
  273. const char *classname = jsclassname.c_str(0);
  274. if (classname)
  275. classname_ = copypath(classname);
  276. }
  277. }
  278. }
  279. }
  280. }
  281. JavaExceptionMessage exceptionmsg(jenv, throwable);
  282. msg_ = copystr(exceptionmsg.message());
  283. }
  284. // More general constructor for handling as a java.lang.RuntimeException
  285. DirectorException(const char *msg) : classname_(0), msg_(copystr(msg ? msg : "Unspecified DirectorException message")) {
  286. }
  287. ~DirectorException() throw() {
  288. delete[] classname_;
  289. delete[] msg_;
  290. }
  291. const char *what() const throw() {
  292. return msg_;
  293. }
  294. // Reconstruct and raise/throw the Java Exception that caused the DirectorException
  295. // Note that any error in the JNI exception handling results in a Java RuntimeException
  296. void raiseJavaException(JNIEnv *jenv) const {
  297. if (jenv) {
  298. jenv->ExceptionClear();
  299. jmethodID ctorMethodID = 0;
  300. jclass throwableclass = 0;
  301. if (classname_) {
  302. throwableclass = jenv->FindClass(classname_);
  303. if (throwableclass)
  304. ctorMethodID = jenv->GetMethodID(throwableclass, "<init>", "(Ljava/lang/String;)V");
  305. }
  306. if (ctorMethodID) {
  307. jenv->ThrowNew(throwableclass, what());
  308. } else {
  309. SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, what());
  310. }
  311. }
  312. }
  313. private:
  314. static char *copypath(const char *srcmsg) {
  315. char *target = copystr(srcmsg);
  316. for (char *c=target; *c; ++c) {
  317. if ('.' == *c)
  318. *c = '/';
  319. }
  320. return target;
  321. }
  322. static char *copystr(const char *srcmsg) {
  323. char *target = 0;
  324. if (srcmsg) {
  325. size_t msglen = strlen(srcmsg) + 1;
  326. target = new char[msglen];
  327. strncpy(target, srcmsg, msglen);
  328. }
  329. return target;
  330. }
  331. const char *classname_;
  332. const char *msg_;
  333. };
  334. // Helper method to determine if a Java throwable matches a particular Java class type
  335. SWIGINTERN bool ExceptionMatches(JNIEnv *jenv, jthrowable throwable, const char *classname) {
  336. bool matches = false;
  337. if (throwable && jenv && classname) {
  338. // Exceptions need to be cleared for correct behavior.
  339. // The caller of ExceptionMatches should restore pending exceptions if desired -
  340. // the caller already has the throwable.
  341. jenv->ExceptionClear();
  342. jclass clz = jenv->FindClass(classname);
  343. if (clz) {
  344. jclass classclz = jenv->GetObjectClass(clz);
  345. jmethodID isInstanceMethodID = jenv->GetMethodID(classclz, "isInstance", "(Ljava/lang/Object;)Z");
  346. if (isInstanceMethodID) {
  347. matches = jenv->CallBooleanMethod(clz, isInstanceMethodID, throwable) != 0;
  348. }
  349. }
  350. #if defined(DEBUG_DIRECTOR_EXCEPTION)
  351. if (jenv->ExceptionCheck()) {
  352. // Typically occurs when an invalid classname argument is passed resulting in a ClassNotFoundException
  353. JavaExceptionMessage exc(jenv, jenv->ExceptionOccurred());
  354. std::cout << "Error: ExceptionMatches: class '" << classname << "' : " << exc.message() << std::endl;
  355. }
  356. #endif
  357. }
  358. return matches;
  359. }
  360. }