2 * Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
6 * http://www.apache.org/licenses/LICENSE-2.0
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
21 #if defined(_MSC_VER) || defined(__WIN32)
22 _DLL_EXPORT DWORD VM_ENV = 0;
24 pthread_key_t JCCEnv::VM_ENV = (pthread_key_t) NULL;
27 #if defined(_MSC_VER) || defined(__WIN32)
29 static CRITICAL_SECTION *mutex = NULL;
34 EnterCriticalSection(mutex);
37 LeaveCriticalSection(mutex);
43 static pthread_mutex_t *mutex = NULL;
48 pthread_mutex_lock(mutex);
51 pthread_mutex_unlock(mutex);
57 JCCEnv::JCCEnv(JavaVM *vm, JNIEnv *vm_env)
59 #if defined(_MSC_VER) || defined(__WIN32)
62 mutex = new CRITICAL_SECTION();
63 InitializeCriticalSection(mutex);
68 mutex = new pthread_mutex_t();
69 pthread_mutex_init(mutex, NULL);
79 void JCCEnv::set_vm(JavaVM *vm, JNIEnv *vm_env)
84 _sys = (jclass) vm_env->NewGlobalRef(vm_env->FindClass("java/lang/System"));
85 _obj = (jclass) vm_env->NewGlobalRef(vm_env->FindClass("java/lang/Object"));
87 _thr = (jclass) vm_env->NewGlobalRef(vm_env->FindClass("org/apache/jcc/PythonException"));
89 _thr = (jclass) vm_env->NewGlobalRef(vm_env->FindClass("java/lang/RuntimeException"));
92 _mids = new jmethodID[max_mid];
94 _mids[mid_sys_identityHashCode] =
95 vm_env->GetStaticMethodID(_sys, "identityHashCode",
96 "(Ljava/lang/Object;)I");
97 _mids[mid_sys_setProperty] =
98 vm_env->GetStaticMethodID(_sys, "setProperty",
99 "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
100 _mids[mid_sys_getProperty] =
101 vm_env->GetStaticMethodID(_sys, "getProperty",
102 "(Ljava/lang/String;)Ljava/lang/String;");
103 _mids[mid_obj_toString] =
104 vm_env->GetMethodID(_obj, "toString",
105 "()Ljava/lang/String;");
106 _mids[mid_obj_hashCode] =
107 vm_env->GetMethodID(_obj, "hashCode",
109 _mids[mid_obj_getClass] =
110 vm_env->GetMethodID(_obj, "getClass",
111 "()Ljava/lang/Class;");
114 jclass iterable = vm_env->FindClass("java/lang/Iterable");
116 if (iterable == NULL) /* JDK < 1.5 */
118 vm_env->ExceptionClear();
119 _mids[mid_iterator] = NULL;
120 _mids[mid_iterator_next] = NULL;
124 _mids[mid_iterator] =
125 vm_env->GetMethodID(iterable,
126 "iterator", "()Ljava/util/Iterator;");
127 _mids[mid_iterator_next] =
128 vm_env->GetMethodID(vm_env->FindClass("java/util/Iterator"),
129 "next", "()Ljava/lang/Object;");
133 _mids[mid_enumeration_nextElement] =
134 vm_env->GetMethodID(vm_env->FindClass("java/util/Enumeration"),
135 "nextElement", "()Ljava/lang/Object;");
137 _mids[mid_Boolean_booleanValue] =
138 vm_env->GetMethodID(vm_env->FindClass("java/lang/Boolean"),
139 "booleanValue", "()Z");
140 _mids[mid_Byte_byteValue] =
141 vm_env->GetMethodID(vm_env->FindClass("java/lang/Byte"),
143 _mids[mid_Character_charValue] =
144 vm_env->GetMethodID(vm_env->FindClass("java/lang/Character"),
146 _mids[mid_Double_doubleValue] =
147 vm_env->GetMethodID(vm_env->FindClass("java/lang/Double"),
148 "doubleValue", "()D");
149 _mids[mid_Float_floatValue] =
150 vm_env->GetMethodID(vm_env->FindClass("java/lang/Float"),
151 "floatValue", "()F");
152 _mids[mid_Integer_intValue] =
153 vm_env->GetMethodID(vm_env->FindClass("java/lang/Integer"),
155 _mids[mid_Long_longValue] =
156 vm_env->GetMethodID(vm_env->FindClass("java/lang/Long"),
158 _mids[mid_Short_shortValue] =
159 vm_env->GetMethodID(vm_env->FindClass("java/lang/Short"),
160 "shortValue", "()S");
163 int JCCEnv::attachCurrentThread(char *name, int asDaemon)
166 JavaVMAttachArgs attach = {
167 JNI_VERSION_1_4, name, NULL
172 result = vm->AttachCurrentThreadAsDaemon((void **) &jenv, &attach);
174 result = vm->AttachCurrentThread((void **) &jenv, &attach);
181 #if defined(_MSC_VER) || defined(__WIN32)
183 void JCCEnv::set_vm_env(JNIEnv *vm_env)
187 TlsSetValue(VM_ENV, (LPVOID) vm_env);
192 void JCCEnv::set_vm_env(JNIEnv *vm_env)
195 pthread_key_create(&VM_ENV, NULL);
196 pthread_setspecific(VM_ENV, (void *) vm_env);
201 jint JCCEnv::getJNIVersion() const
203 return get_vm_env()->GetVersion();
206 jstring JCCEnv::getJavaVersion() const
209 callStaticObjectMethod(_sys, _mids[mid_sys_getProperty],
210 get_vm_env()->NewStringUTF("java.version"));
213 jobject JCCEnv::iterator(jobject obj) const
215 return callObjectMethod(obj, _mids[mid_iterator]);
218 jobject JCCEnv::iteratorNext(jobject obj) const
220 return callObjectMethod(obj, _mids[mid_iterator_next]);
223 jobject JCCEnv::enumerationNext(jobject obj) const
225 return callObjectMethod(obj, _mids[mid_enumeration_nextElement]);
228 jboolean JCCEnv::isInstanceOf(jobject obj, getclassfn initializeClass) const
230 return get_vm_env()->IsInstanceOf(obj, (*initializeClass)());
233 jclass JCCEnv::findClass(const char *className) const
239 JNIEnv *vm_env = get_vm_env();
242 cls = vm_env->FindClass(className);
248 PyErr_SetString(PyExc_RuntimeError, "attachCurrentThread() must be called first");
261 PyErr_SetString(PyExc_RuntimeError, "initVM() must be called first");
274 void JCCEnv::registerNatives(jclass cls, JNINativeMethod *methods, int n) const
276 get_vm_env()->RegisterNatives(cls, methods, n);
279 jobject JCCEnv::newGlobalRef(jobject obj, int id)
283 if (id) /* zero when weak global ref is desired */
287 for (std::multimap<int, countedRef>::iterator iter = refs.find(id);
290 if (iter->first != id)
292 if (isSame(obj, iter->second.global))
294 /* If it's in the table but not the same reference,
295 * it must be a local reference and must be deleted.
297 if (obj != iter->second.global)
298 get_vm_env()->DeleteLocalRef(obj);
300 iter->second.count += 1;
301 return iter->second.global;
305 JNIEnv *vm_env = get_vm_env();
308 ref.global = vm_env->NewGlobalRef(obj);
310 refs.insert(std::pair<const int, countedRef>(id, ref));
311 vm_env->DeleteLocalRef(obj);
316 return (jobject) get_vm_env()->NewWeakGlobalRef(obj);
322 jobject JCCEnv::deleteGlobalRef(jobject obj, int id)
326 if (id) /* zero when obj is weak global ref */
330 for (std::multimap<int, countedRef>::iterator iter = refs.find(id);
333 if (iter->first != id)
335 if (isSame(obj, iter->second.global))
337 if (iter->second.count == 1)
339 JNIEnv *vm_env = get_vm_env();
343 /* Python's cyclic garbage collector may remove
344 * an object inside a thread that is not attached
345 * to the JVM. This makes sure the JVM doesn't
348 attachCurrentThread(NULL, 0);
349 vm_env = get_vm_env();
352 vm_env->DeleteGlobalRef(iter->second.global);
356 iter->second.count -= 1;
362 printf("deleting non-existent ref: 0x%x\n", id);
365 get_vm_env()->DeleteWeakGlobalRef((jweak) obj);
371 jobject JCCEnv::newObject(getclassfn initializeClass, jmethodID **mids,
374 jclass cls = (*initializeClass)();
375 JNIEnv *vm_env = get_vm_env();
383 obj = vm_env->NewObjectV(cls, (*mids)[m], ap);
391 PyErr_SetString(PyExc_RuntimeError, "attachCurrentThread() must be called first");
404 jobjectArray JCCEnv::newObjectArray(jclass cls, int size)
406 jobjectArray array = get_vm_env()->NewObjectArray(size, cls, NULL);
412 void JCCEnv::setObjectArrayElement(jobjectArray array, int n,
415 get_vm_env()->SetObjectArrayElement(array, n, obj);
419 jobject JCCEnv::getObjectArrayElement(jobjectArray array, int n) const
421 jobject obj = get_vm_env()->GetObjectArrayElement(array, n);
427 int JCCEnv::getArrayLength(jarray array) const
429 int len = get_vm_env()->GetArrayLength(array);
436 jclass JCCEnv::getPythonExceptionClass() const
442 void JCCEnv::reportException() const
444 JNIEnv *vm_env = get_vm_env();
445 jthrowable throwable = vm_env->ExceptionOccurred();
450 vm_env->ExceptionDescribe();
455 if (PyErr_Occurred())
457 /* _thr is PythonException ifdef _jcc_lib (shared mode)
458 * if not shared mode, _thr is RuntimeException
460 jobject cls = (jobject) vm_env->GetObjectClass(throwable);
462 if (vm_env->IsSameObject(cls, _thr))
465 /* PythonException class is not available without shared mode.
466 * Python exception information thus gets lost and exception
467 * is reported via plain Java RuntimeException.
483 #define DEFINE_CALL(jtype, Type) \
484 jtype JCCEnv::call##Type##Method(jobject obj, \
485 jmethodID mid, ...) const \
491 result = get_vm_env()->Call##Type##MethodV(obj, mid, ap); \
499 #define DEFINE_NONVIRTUAL_CALL(jtype, Type) \
500 jtype JCCEnv::callNonvirtual##Type##Method(jobject obj, jclass cls, \
501 jmethodID mid, ...) const \
507 result = get_vm_env()->CallNonvirtual##Type##MethodV(obj, cls, \
516 #define DEFINE_STATIC_CALL(jtype, Type) \
517 jtype JCCEnv::callStatic##Type##Method(jclass cls, \
518 jmethodID mid, ...) const \
524 result = get_vm_env()->CallStatic##Type##MethodV(cls, mid, ap); \
532 DEFINE_CALL(jobject, Object)
533 DEFINE_CALL(jboolean, Boolean)
534 DEFINE_CALL(jbyte, Byte)
535 DEFINE_CALL(jchar, Char)
536 DEFINE_CALL(jdouble, Double)
537 DEFINE_CALL(jfloat, Float)
538 DEFINE_CALL(jint, Int)
539 DEFINE_CALL(jlong, Long)
540 DEFINE_CALL(jshort, Short)
542 DEFINE_NONVIRTUAL_CALL(jobject, Object)
543 DEFINE_NONVIRTUAL_CALL(jboolean, Boolean)
544 DEFINE_NONVIRTUAL_CALL(jbyte, Byte)
545 DEFINE_NONVIRTUAL_CALL(jchar, Char)
546 DEFINE_NONVIRTUAL_CALL(jdouble, Double)
547 DEFINE_NONVIRTUAL_CALL(jfloat, Float)
548 DEFINE_NONVIRTUAL_CALL(jint, Int)
549 DEFINE_NONVIRTUAL_CALL(jlong, Long)
550 DEFINE_NONVIRTUAL_CALL(jshort, Short)
552 DEFINE_STATIC_CALL(jobject, Object)
553 DEFINE_STATIC_CALL(jboolean, Boolean)
554 DEFINE_STATIC_CALL(jbyte, Byte)
555 DEFINE_STATIC_CALL(jchar, Char)
556 DEFINE_STATIC_CALL(jdouble, Double)
557 DEFINE_STATIC_CALL(jfloat, Float)
558 DEFINE_STATIC_CALL(jint, Int)
559 DEFINE_STATIC_CALL(jlong, Long)
560 DEFINE_STATIC_CALL(jshort, Short)
562 void JCCEnv::callVoidMethod(jobject obj, jmethodID mid, ...) const
567 get_vm_env()->CallVoidMethodV(obj, mid, ap);
573 void JCCEnv::callNonvirtualVoidMethod(jobject obj, jclass cls,
574 jmethodID mid, ...) const
579 get_vm_env()->CallNonvirtualVoidMethodV(obj, cls, mid, ap);
585 void JCCEnv::callStaticVoidMethod(jclass cls, jmethodID mid, ...) const
590 get_vm_env()->CallStaticVoidMethodV(cls, mid, ap);
597 jboolean JCCEnv::booleanValue(jobject obj) const
599 return get_vm_env()->CallBooleanMethod(obj, _mids[mid_Boolean_booleanValue]);
602 jbyte JCCEnv::byteValue(jobject obj) const
604 return get_vm_env()->CallByteMethod(obj, _mids[mid_Byte_byteValue]);
607 jchar JCCEnv::charValue(jobject obj) const
609 return get_vm_env()->CallCharMethod(obj, _mids[mid_Character_charValue]);
612 jdouble JCCEnv::doubleValue(jobject obj) const
614 return get_vm_env()->CallDoubleMethod(obj, _mids[mid_Double_doubleValue]);
617 jfloat JCCEnv::floatValue(jobject obj) const
619 return get_vm_env()->CallFloatMethod(obj, _mids[mid_Float_floatValue]);
622 jint JCCEnv::intValue(jobject obj) const
624 return get_vm_env()->CallIntMethod(obj, _mids[mid_Integer_intValue]);
627 jlong JCCEnv::longValue(jobject obj) const
629 return get_vm_env()->CallLongMethod(obj, _mids[mid_Long_longValue]);
632 jshort JCCEnv::shortValue(jobject obj) const
634 return get_vm_env()->CallShortMethod(obj, _mids[mid_Short_shortValue]);
638 jmethodID JCCEnv::getMethodID(jclass cls, const char *name,
639 const char *signature) const
641 jmethodID id = get_vm_env()->GetMethodID(cls, name, signature);
648 jfieldID JCCEnv::getFieldID(jclass cls, const char *name,
649 const char *signature) const
651 jfieldID id = get_vm_env()->GetFieldID(cls, name, signature);
659 jmethodID JCCEnv::getStaticMethodID(jclass cls, const char *name,
660 const char *signature) const
662 jmethodID id = get_vm_env()->GetStaticMethodID(cls, name, signature);
669 jobject JCCEnv::getStaticObjectField(jclass cls, const char *name,
670 const char *signature) const
672 JNIEnv *vm_env = get_vm_env();
673 jfieldID id = vm_env->GetStaticFieldID(cls, name, signature);
677 return vm_env->GetStaticObjectField(cls, id);
680 #define DEFINE_GET_STATIC_FIELD(jtype, Type, signature) \
681 jtype JCCEnv::getStatic##Type##Field(jclass cls, \
682 const char *name) const \
684 JNIEnv *vm_env = get_vm_env(); \
685 jfieldID id = vm_env->GetStaticFieldID(cls, name, #signature); \
687 return vm_env->GetStatic##Type##Field(cls, id); \
690 DEFINE_GET_STATIC_FIELD(jboolean, Boolean, Z)
691 DEFINE_GET_STATIC_FIELD(jbyte, Byte, B)
692 DEFINE_GET_STATIC_FIELD(jchar, Char, C)
693 DEFINE_GET_STATIC_FIELD(jdouble, Double, D)
694 DEFINE_GET_STATIC_FIELD(jfloat, Float, F)
695 DEFINE_GET_STATIC_FIELD(jint, Int, I)
696 DEFINE_GET_STATIC_FIELD(jlong, Long, J)
697 DEFINE_GET_STATIC_FIELD(jshort, Short, S)
699 #define DEFINE_GET_FIELD(jtype, Type) \
700 jtype JCCEnv::get##Type##Field(jobject obj, jfieldID id) const \
702 jtype value = get_vm_env()->Get##Type##Field(obj, id); \
707 DEFINE_GET_FIELD(jobject, Object)
708 DEFINE_GET_FIELD(jboolean, Boolean)
709 DEFINE_GET_FIELD(jbyte, Byte)
710 DEFINE_GET_FIELD(jchar, Char)
711 DEFINE_GET_FIELD(jdouble, Double)
712 DEFINE_GET_FIELD(jfloat, Float)
713 DEFINE_GET_FIELD(jint, Int)
714 DEFINE_GET_FIELD(jlong, Long)
715 DEFINE_GET_FIELD(jshort, Short)
717 #define DEFINE_SET_FIELD(jtype, Type) \
718 void JCCEnv::set##Type##Field(jobject obj, jfieldID id, \
721 get_vm_env()->Set##Type##Field(obj, id, value); \
725 DEFINE_SET_FIELD(jobject, Object)
726 DEFINE_SET_FIELD(jboolean, Boolean)
727 DEFINE_SET_FIELD(jbyte, Byte)
728 DEFINE_SET_FIELD(jchar, Char)
729 DEFINE_SET_FIELD(jdouble, Double)
730 DEFINE_SET_FIELD(jfloat, Float)
731 DEFINE_SET_FIELD(jint, Int)
732 DEFINE_SET_FIELD(jlong, Long)
733 DEFINE_SET_FIELD(jshort, Short)
735 void JCCEnv::setClassPath(const char *classPath)
737 JNIEnv *vm_env = get_vm_env();
738 jclass _ucl = (jclass) vm_env->FindClass("java/net/URLClassLoader");
739 jclass _fil = (jclass) vm_env->FindClass("java/io/File");
740 jmethodID mid = vm_env->GetStaticMethodID(_ucl, "getSystemClassLoader",
741 "()Ljava/lang/ClassLoader;");
742 jobject classLoader = vm_env->CallStaticObjectMethod(_ucl, mid);
743 jmethodID mf = vm_env->GetMethodID(_fil, "<init>", "(Ljava/lang/String;)V");
744 jmethodID mu = vm_env->GetMethodID(_fil, "toURL", "()Ljava/net/URL;");
745 jmethodID ma = vm_env->GetMethodID(_ucl, "addURL", "(Ljava/net/URL;)V");
746 #if defined(_MSC_VER) || defined(__WIN32)
748 char *path = _strdup(classPath);
751 char *path = strdup(classPath);
754 for (char *cp = strtok(path, pathsep);
756 cp = strtok(NULL, pathsep)) {
757 jstring string = vm_env->NewStringUTF(cp);
758 jobject file = vm_env->NewObject(_fil, mf, string);
759 jobject url = vm_env->CallObjectMethod(file, mu);
761 vm_env->CallVoidMethod(classLoader, ma, url);
766 char *JCCEnv::getClassPath()
768 JNIEnv *vm_env = get_vm_env();
769 jclass _ucl = (jclass) vm_env->FindClass("java/net/URLClassLoader");
770 jclass _url = (jclass) vm_env->FindClass("java/net/URL");
771 jmethodID mid = vm_env->GetStaticMethodID(_ucl, "getSystemClassLoader",
772 "()Ljava/lang/ClassLoader;");
773 jobject classLoader = vm_env->CallStaticObjectMethod(_ucl, mid);
774 jmethodID gu = vm_env->GetMethodID(_ucl, "getURLs", "()[Ljava/net/URL;");
775 jmethodID gp = vm_env->GetMethodID(_url, "getPath", "()Ljava/lang/String;");
776 #if defined(_MSC_VER) || defined(__WIN32)
781 jobjectArray array = (jobjectArray)
782 vm_env->CallObjectMethod(classLoader, gu);
783 int count = array ? vm_env->GetArrayLength(array) : 0;
784 int first = 1, total = 0;
785 char *classpath = NULL;
787 for (int i = 0; i < count; i++) {
788 jobject url = vm_env->GetObjectArrayElement(array, i);
789 jstring path = (jstring) vm_env->CallObjectMethod(url, gp);
790 const char *chars = vm_env->GetStringUTFChars(path, NULL);
791 int size = vm_env->GetStringUTFLength(path);
794 if (classpath == NULL)
795 classpath = (char *) calloc(total, 1);
797 classpath = (char *) realloc(classpath, total);
798 if (classpath == NULL)
804 strcat(classpath, pathsep);
806 strcat(classpath, chars);
812 jstring JCCEnv::fromUTF(const char *bytes) const
814 jstring str = get_vm_env()->NewStringUTF(bytes);
821 char *JCCEnv::toUTF(jstring str) const
823 JNIEnv *vm_env = get_vm_env();
824 int len = vm_env->GetStringUTFLength(str);
825 char *bytes = new char[len + 1];
827 const char *utf = vm_env->GetStringUTFChars(str, &isCopy);
832 memcpy(bytes, utf, len);
835 vm_env->ReleaseStringUTFChars(str, utf);
840 char *JCCEnv::toString(jobject obj) const
844 ? toUTF((jstring) callObjectMethod(obj, _mids[mid_obj_toString]))
851 JNIEnv *vm_env = get_vm_env();
853 vm_env->ExceptionDescribe();
854 vm_env->ExceptionClear();
864 char *JCCEnv::getClassName(jobject obj) const
867 ? toString(callObjectMethod(obj, _mids[mid_obj_getClass]))
873 jstring JCCEnv::fromPyString(PyObject *object) const
875 if (object == Py_None)
878 if (PyUnicode_Check(object))
880 if (sizeof(Py_UNICODE) == sizeof(jchar))
882 jchar *buf = (jchar *) PyUnicode_AS_UNICODE(object);
883 jsize len = (jsize) PyUnicode_GET_SIZE(object);
885 return get_vm_env()->NewString(buf, len);
889 jsize len = PyUnicode_GET_SIZE(object);
890 Py_UNICODE *pchars = PyUnicode_AS_UNICODE(object);
891 jchar *jchars = new jchar[len];
894 for (int i = 0; i < len; i++)
895 jchars[i] = (jchar) pchars[i];
897 str = get_vm_env()->NewString(jchars, len);
903 else if (PyString_Check(object))
904 return fromUTF(PyString_AS_STRING(object));
907 PyObject *tuple = Py_BuildValue("(sO)", "expected a string", object);
909 PyErr_SetObject(PyExc_TypeError, tuple);
916 PyObject *JCCEnv::fromJString(jstring js, int delete_local_ref) const
921 JNIEnv *vm_env = get_vm_env();
924 if (sizeof(Py_UNICODE) == sizeof(jchar))
927 const jchar *buf = vm_env->GetStringChars(js, &isCopy);
928 jsize len = vm_env->GetStringLength(js);
930 string = PyUnicode_FromUnicode((const Py_UNICODE *) buf, len);
931 vm_env->ReleaseStringChars(js, buf);
935 jsize len = vm_env->GetStringLength(js);
937 string = PyUnicode_FromUnicode(NULL, len);
941 const jchar *jchars = vm_env->GetStringChars(js, &isCopy);
942 Py_UNICODE *pchars = PyUnicode_AS_UNICODE(string);
944 for (int i = 0; i < len; i++)
945 pchars[i] = (Py_UNICODE) jchars[i];
947 vm_env->ReleaseStringChars(js, jchars);
951 if (delete_local_ref)
952 vm_env->DeleteLocalRef((jobject) js);
958 /* may be called from finalizer thread which has no vm_env thread local */
959 void JCCEnv::finalizeObject(JNIEnv *jenv, PyObject *obj)