diff --git a/jpype/_core.py b/jpype/_core.py index 219cb2664..d279b85e3 100644 --- a/jpype/_core.py +++ b/jpype/_core.py @@ -210,7 +210,7 @@ def startJVM(*args, **kwargs): try: _jpype.startup(jvmpath, tuple(args), ignoreUnrecognized, convertStrings) - _JVM_started = True + initializeResources() except RuntimeError as ex: source = str(ex) if "UnsupportedClassVersion" in source: @@ -222,6 +222,9 @@ def startJVM(*args, **kwargs): jvmpath, version)) from ex raise + +def initializeResources(): + global _JVM_started _jpype._java_lang_Class = None _jpype._java_lang_Object = _jpype.JClass("java.lang.Object") _jpype._java_lang_Throwable = _jpype.JClass("java.lang.Throwable") @@ -299,6 +302,9 @@ def startJVM(*args, **kwargs): _jpype.JClass('org.jpype.JPypeKeywords').setKeywords( list(_pykeywords._KEYWORDS)) + # Everything successed so started is now true. + _JVM_started = True + def shutdownJVM(): """ Shuts down the JVM. diff --git a/jpype/pickle.py b/jpype/pickle.py index 4ee8fd47a..34726a083 100644 --- a/jpype/pickle.py +++ b/jpype/pickle.py @@ -82,10 +82,7 @@ class that can produce reducers as needed. """ def __init__(self, dispatch): - cl = _jpype.JClass( - 'org.jpype.classloader.JPypeClassLoader').getInstance() - self._encoder = _jpype.JClass( - cl.loadClass('org.jpype.pickle.Encoder'))() + self._encoder = _jpype.JClass('org.jpype.pickle.Encoder')() self._builder = JUnserializer() self._dispatch = dispatch @@ -152,10 +149,7 @@ class does not match, usually as a result of a new jar """ def __init__(self, file, *args, **kwargs): - cl = _jpype.JClass( - 'org.jpype.classloader.JPypeClassLoader').getInstance() - self._decoder = _jpype.JClass( - cl.loadClass('org.jpype.pickle.Decoder'))() + self._decoder = _jpype.JClass('org.jpype.pickle.Decoder')() pickle.Unpickler.__init__(self, file, *args, **kwargs) def find_class(self, module, cls): diff --git a/native/common/include/jp_arrayclass.h b/native/common/include/jp_arrayclass.h index feceafb19..9ffb325f8 100644 --- a/native/common/include/jp_arrayclass.h +++ b/native/common/include/jp_arrayclass.h @@ -34,7 +34,7 @@ class JPArrayClass : public JPClass virtual JPMatch::Type findJavaConversion(JPMatch &match) override; virtual void getConversionInfo(JPConversionInfo &info) override; - JPValue newInstance(JPJavaFrame& frame, int length); + JPValue newArray(JPJavaFrame& frame, int length); /** * Create a new java array containing a set of items take from diff --git a/native/common/include/jp_booleantype.h b/native/common/include/jp_booleantype.h index acc985e87..b0215ad49 100755 --- a/native/common/include/jp_booleantype.h +++ b/native/common/include/jp_booleantype.h @@ -36,7 +36,7 @@ class JPBooleanType : public JPPrimitiveType return v.z; } - virtual JPClass* getBoxedClass(JPContext *context) const + virtual JPClass* getBoxedClass(JPContext *context) const override { return context->_java_lang_Boolean; } @@ -54,7 +54,7 @@ class JPBooleanType : public JPPrimitiveType virtual JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; virtual void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; - virtual jarray newArrayInstance(JPJavaFrame& frame, jsize size) override; + virtual jarray newArrayOf(JPJavaFrame& frame, jsize size) override; virtual void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject *sequence) override; diff --git a/native/common/include/jp_bytetype.h b/native/common/include/jp_bytetype.h index 35fbf450e..0f35aec9d 100755 --- a/native/common/include/jp_bytetype.h +++ b/native/common/include/jp_bytetype.h @@ -55,7 +55,7 @@ class JPByteType : public JPPrimitiveType virtual JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; virtual void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; - virtual jarray newArrayInstance(JPJavaFrame& frame, jsize size) override; + virtual jarray newArrayOf(JPJavaFrame& frame, jsize size) override; virtual void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject*) override; virtual JPPyObject getArrayItem(JPJavaFrame& frame, jarray, jsize ndx) override; virtual void setArrayItem(JPJavaFrame& frame, jarray, jsize ndx, PyObject* val) override; diff --git a/native/common/include/jp_chartype.h b/native/common/include/jp_chartype.h index 5c94d0a94..acd140105 100755 --- a/native/common/include/jp_chartype.h +++ b/native/common/include/jp_chartype.h @@ -39,7 +39,7 @@ class JPCharType : public JPPrimitiveType return v.c; } - virtual JPClass* getBoxedClass(JPContext *context) const + virtual JPClass* getBoxedClass(JPContext *context) const override { return context->_java_lang_Character; } @@ -57,7 +57,7 @@ class JPCharType : public JPPrimitiveType virtual JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; virtual void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; - virtual jarray newArrayInstance(JPJavaFrame& frame, jsize size) override; + virtual jarray newArrayOf(JPJavaFrame& frame, jsize size) override; virtual void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject*) override; virtual JPPyObject getArrayItem(JPJavaFrame& frame, jarray, jsize ndx) override; virtual void setArrayItem(JPJavaFrame& frame, jarray, jsize ndx, PyObject* val) override; diff --git a/native/common/include/jp_class.h b/native/common/include/jp_class.h index 95c5e572a..5c1419ca1 100644 --- a/native/common/include/jp_class.h +++ b/native/common/include/jp_class.h @@ -176,7 +176,7 @@ class JPClass : public JPResource virtual void setField(JPJavaFrame& frame, jobject obj, jfieldID fid, PyObject* val); JPClass* newArrayType(JPJavaFrame &frame, long d); - virtual jarray newArrayInstance(JPJavaFrame& frame, jsize size); + virtual jarray newArrayOf(JPJavaFrame& frame, jsize size); virtual void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject* vals); virtual JPPyObject getArrayItem(JPJavaFrame& frame, jarray, jsize ndx); diff --git a/native/common/include/jp_classloader.h b/native/common/include/jp_classloader.h index 064cee9cb..841198a1c 100644 --- a/native/common/include/jp_classloader.h +++ b/native/common/include/jp_classloader.h @@ -47,9 +47,10 @@ class JPClassLoader private: JPContext* m_Context; + JPClassRef m_ClassClass; JPObjectRef m_SystemClassLoader; JPObjectRef m_BootLoader; - jmethodID m_FindClass; + jmethodID m_ForNameID; } ; #endif // _JPCLASSLOADER_H_ \ No newline at end of file diff --git a/native/common/include/jp_context.h b/native/common/include/jp_context.h index 795a6e1e2..3b9fe4d3f 100644 --- a/native/common/include/jp_context.h +++ b/native/common/include/jp_context.h @@ -114,7 +114,6 @@ class JPContext friend class JPJavaFrame; friend class JPypeException; friend class JPClass; - friend class JPTypeFactory; JPContext(); virtual ~JPContext(); @@ -122,6 +121,8 @@ class JPContext // JVM control functions bool isRunning(); void startJVM(const string& vmPath, const StringVector& args, bool ignoreUnrecognized, bool convertStrings); + void attachJVM(JNIEnv* env); + void initializeResources(JNIEnv* env); void shutdownJVM(); void attachCurrentThread(); void attachCurrentThreadAsDaemon(); @@ -159,11 +160,6 @@ class JPContext return m_ClassLoader; } - JPReferenceQueue* getReferenceQueue() - { - return m_ReferenceQueue; - } - bool getConvertStrings() const { return m_ConvertStrings; @@ -196,6 +192,7 @@ class JPContext JPClass* _java_lang_reflect_Method; JPClass* _java_lang_Throwable; JPStringType* _java_lang_String; + JPClass* _java_nio_ByteBuffer; private: @@ -204,8 +201,6 @@ class JPContext jint(JNICALL * CreateJVM_Method)(JavaVM **pvm, void **penv, void *args); jint(JNICALL * GetCreatedJVMs_Method)(JavaVM **pvm, jsize size, jsize * nVms); - static JNIEXPORT void JNICALL onShutdown(JNIEnv *env, jobject obj, jlong contextPtr); - private: JPContext(const JPContext& orig); @@ -215,10 +210,8 @@ class JPContext JPObjectRef m_JavaContext; // Services - JPTypeFactory *m_TypeFactory; JPTypeManager *m_TypeManager; JPClassLoader *m_ClassLoader; - JPReferenceQueue *m_ReferenceQueue; public: JPClassRef m_RuntimeException; @@ -269,9 +262,12 @@ class JPContext jmethodID m_FloatValueID; jmethodID m_DoubleValueID; + void onShutdown(); + private: bool m_Running; bool m_ConvertStrings; + bool m_Embedded; public: JPGarbageCollection *m_GC; } ; @@ -328,4 +324,4 @@ JPRef& JPRef::operator=(const JPRef& other) return *this; } -#endif /* JP_CONTEXT_H */ \ No newline at end of file +#endif /* JP_CONTEXT_H */ diff --git a/native/common/include/jp_doubletype.h b/native/common/include/jp_doubletype.h index 7c753d769..9e0e6222f 100755 --- a/native/common/include/jp_doubletype.h +++ b/native/common/include/jp_doubletype.h @@ -37,7 +37,7 @@ class JPDoubleType : public JPPrimitiveType return v.d; } - virtual JPClass* getBoxedClass(JPContext *context) const + virtual JPClass* getBoxedClass(JPContext *context) const override { return context->_java_lang_Double; } @@ -55,7 +55,7 @@ class JPDoubleType : public JPPrimitiveType virtual JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; virtual void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; - virtual jarray newArrayInstance(JPJavaFrame& frame, jsize size) override; + virtual jarray newArrayOf(JPJavaFrame& frame, jsize size) override; virtual void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject*) override; virtual JPPyObject getArrayItem(JPJavaFrame& frame, jarray, jsize ndx) override; diff --git a/native/common/include/jp_floattype.h b/native/common/include/jp_floattype.h index 2e98ab2ab..56edb90f9 100755 --- a/native/common/include/jp_floattype.h +++ b/native/common/include/jp_floattype.h @@ -37,7 +37,7 @@ class JPFloatType : public JPPrimitiveType return v.f; } - virtual JPClass* getBoxedClass(JPContext *context) const + virtual JPClass* getBoxedClass(JPContext *context) const override { return context->_java_lang_Float; } @@ -55,7 +55,7 @@ class JPFloatType : public JPPrimitiveType virtual JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; virtual void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; - virtual jarray newArrayInstance(JPJavaFrame& frame, jsize size) override; + virtual jarray newArrayOf(JPJavaFrame& frame, jsize size) override; virtual void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject* sequence) override; diff --git a/native/common/include/jp_inttype.h b/native/common/include/jp_inttype.h index bee8d9e23..2d7876a83 100755 --- a/native/common/include/jp_inttype.h +++ b/native/common/include/jp_inttype.h @@ -37,7 +37,7 @@ class JPIntType : public JPPrimitiveType return v.i; } - virtual JPClass* getBoxedClass(JPContext *context) const + virtual JPClass* getBoxedClass(JPContext *context) const override { return context->_java_lang_Integer; } @@ -55,7 +55,7 @@ class JPIntType : public JPPrimitiveType virtual JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; virtual void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; - virtual jarray newArrayInstance(JPJavaFrame& frame, jsize size) override; + virtual jarray newArrayOf(JPJavaFrame& frame, jsize size) override; virtual void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject* sequence) override; diff --git a/native/common/include/jp_javaframe.h b/native/common/include/jp_javaframe.h index 5c14890f4..2eeb72564 100644 --- a/native/common/include/jp_javaframe.h +++ b/native/common/include/jp_javaframe.h @@ -366,6 +366,7 @@ class JPJavaFrame void ReleaseDoubleArrayElements(jdoubleArray, jdouble* v, jint mode); // Object + jclass GetObjectClass(jobject obj); jobject GetStaticObjectField(jclass clazz, jfieldID fid); jobject GetObjectField(jobject clazz, jfieldID fid); void SetStaticObjectField(jclass clazz, jfieldID fid, jobject val); @@ -397,6 +398,8 @@ class JPJavaFrame jarray getPackageContents(jobject pkg); void newWrapper(JPClass* cls); + void registerRef(jobject obj, PyObject* hostRef); + void registerRef(jobject obj, void* ref, JCleanupHook cleanup); } ; diff --git a/native/common/include/jp_longtype.h b/native/common/include/jp_longtype.h index 57a0ee5c4..6319ac60d 100755 --- a/native/common/include/jp_longtype.h +++ b/native/common/include/jp_longtype.h @@ -36,7 +36,7 @@ class JPLongType : public JPPrimitiveType return v.j; } - virtual JPClass* getBoxedClass(JPContext *context) const + virtual JPClass* getBoxedClass(JPContext *context) const override { return context->_java_lang_Long; } @@ -54,7 +54,7 @@ class JPLongType : public JPPrimitiveType virtual JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; virtual void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; - virtual jarray newArrayInstance(JPJavaFrame& frame, jsize size) override; + virtual jarray newArrayOf(JPJavaFrame& frame, jsize size) override; virtual void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject *sequence) override; diff --git a/native/common/include/jp_primitive_accessor.h b/native/common/include/jp_primitive_accessor.h index e587941d0..cac1513d4 100644 --- a/native/common/include/jp_primitive_accessor.h +++ b/native/common/include/jp_primitive_accessor.h @@ -57,6 +57,11 @@ class JPPrimitiveArrayAccessor } } + jsize size() + { + return _frame.GetArrayLength(_array); + } + ptr_t get() { return _elem; @@ -98,11 +103,11 @@ template PyObject *convertMultiArray( } // Reserve space for array. - jobjectArray contents = (jobjectArray) context->_java_lang_Object->newArrayInstance(frame, subs); + jobjectArray contents = (jobjectArray) context->_java_lang_Object->newArrayOf(frame, subs); std::vector indices(view.ndim); int u = view.ndim - 1; int k = 0; - jarray a0 = cls->newArrayInstance(frame, base); + jarray a0 = cls->newArrayOf(frame, base); frame.SetObjectArrayElement(contents, k++, a0); jboolean isCopy; void *mem = frame.getEnv()->GetPrimitiveArrayCritical(a0, &isCopy); @@ -141,7 +146,7 @@ template PyObject *convertMultiArray( if (j == u) break; - a0 = cls->newArrayInstance(frame, base); + a0 = cls->newArrayOf(frame, base); frame.SetObjectArrayElement(contents, k++, a0); mem = frame.getEnv()->GetPrimitiveArrayCritical(a0, &isCopy); JP_TRACE_JAVA("GetPrimitiveArrayCritical", mem); @@ -171,7 +176,7 @@ class JPConversionLong : public JPIndexConversion { public: - virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) + virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) override { if (!PyLong_CheckExact(match.object) && !PyIndex_Check(match.object)) return match.type = JPMatch::_none; @@ -204,7 +209,7 @@ class JPConversionLongNumber : public JPConversionLong { public: - virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) + virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) override { if (!PyNumber_Check(match.object)) return match.type = JPMatch::_none; diff --git a/native/common/include/jp_proxy.h b/native/common/include/jp_proxy.h index 4b5a58e97..04edeba34 100644 --- a/native/common/include/jp_proxy.h +++ b/native/common/include/jp_proxy.h @@ -41,13 +41,6 @@ class JPProxy virtual JPPyObject getCallable(const string& cname) = 0; static void releaseProxyPython(void* host); - static JNIEXPORT jobject JNICALL hostInvoke( - JNIEnv *env, jclass clazz, - jlong contextPtr, jstring name, - jlong hostObj, - jlong returnTypePtr, - jlongArray parameterTypePtrs, - jobjectArray args); protected: JPContext* m_Context; diff --git a/native/common/include/jp_reference_queue.h b/native/common/include/jp_reference_queue.h index 44a90a91a..df1e10ded 100644 --- a/native/common/include/jp_reference_queue.h +++ b/native/common/include/jp_reference_queue.h @@ -17,25 +17,10 @@ #define JP_REFERENCE_QUEUE_H__ #include -extern "C" +namespace JPReferenceQueue { -typedef void (*JCleanupHook)(void*) ; -} - -class JPReferenceQueue -{ - friend class JPContext; -public: - explicit JPReferenceQueue(JPJavaFrame& frame); - ~JPReferenceQueue(); - void registerRef( jobject obj, PyObject* targetRef); - void registerRef(jobject obj, void* host, JCleanupHook func); - -private: - JPContext* m_Context; - JPObjectRef m_ReferenceQueue; - jmethodID m_ReferenceQueueRegisterMethod; - +void registerRef(JPJavaFrame &frame, jobject obj, PyObject* targetRef); +void registerRef(JPJavaFrame &frame, jobject obj, void* host, JCleanupHook func); } ; // end of namespace JPReferenceQueue #endif \ No newline at end of file diff --git a/native/common/include/jp_shorttype.h b/native/common/include/jp_shorttype.h index e44efc0b9..ab9448a34 100755 --- a/native/common/include/jp_shorttype.h +++ b/native/common/include/jp_shorttype.h @@ -36,7 +36,7 @@ class JPShortType : public JPPrimitiveType return v.s; } - virtual JPClass* getBoxedClass(JPContext *context) const + virtual JPClass* getBoxedClass(JPContext *context) const override { return context->_java_lang_Short; } @@ -54,7 +54,7 @@ class JPShortType : public JPPrimitiveType virtual JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; virtual void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; - virtual jarray newArrayInstance(JPJavaFrame& frame, jsize size) override; + virtual jarray newArrayOf(JPJavaFrame& frame, jsize size) override; virtual void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject *sequence) override; diff --git a/native/common/include/jp_typemanager.h b/native/common/include/jp_typemanager.h index ac245d8b1..5cec58d4c 100644 --- a/native/common/include/jp_typemanager.h +++ b/native/common/include/jp_typemanager.h @@ -19,12 +19,6 @@ /** * These functions will manage the cache of found type, be it primitive types, class types or the "magic" types. */ -class JPTypeFactory -{ -public: - explicit JPTypeFactory(JPJavaFrame& frame); - ~JPTypeFactory(); -} ; class JPTypeManager { diff --git a/native/common/include/jp_value.h b/native/common/include/jp_value.h index 31d12f7b2..78c48ba8e 100644 --- a/native/common/include/jp_value.h +++ b/native/common/include/jp_value.h @@ -84,7 +84,6 @@ class JPValue private: JPClass* m_Class; jvalue m_Value; - jmethodID m_GetInvocationHandlerID; } ; #endif // _JPVALUE_H_ diff --git a/native/common/include/jp_voidtype.h b/native/common/include/jp_voidtype.h index 722110b9b..98ed741cc 100755 --- a/native/common/include/jp_voidtype.h +++ b/native/common/include/jp_voidtype.h @@ -23,7 +23,7 @@ class JPVoidType : public JPPrimitiveType JPVoidType(); virtual ~JPVoidType(); - virtual JPClass* getBoxedClass(JPContext *context) const + virtual JPClass* getBoxedClass(JPContext *context) const override { return context->_java_lang_Void; } @@ -39,7 +39,7 @@ class JPVoidType : public JPPrimitiveType virtual JPPyObject getField(JPJavaFrame& frame, jobject c, jfieldID fid) override; virtual void setField(JPJavaFrame& frame, jobject c, jfieldID fid, PyObject* val) override; - virtual jarray newArrayInstance(JPJavaFrame& frame, jsize size) override; + virtual jarray newArrayOf(JPJavaFrame& frame, jsize size) override; virtual void setArrayRange(JPJavaFrame& frame, jarray, jsize start, jsize length, jsize step, PyObject *sequence) override; @@ -59,7 +59,7 @@ class JPVoidType : public JPPrimitiveType jarray a, jsize start, jsize len, void* memory, int offset) override; virtual PyObject *newMultiArray(JPJavaFrame &frame, - JPPyBuffer& view, int subs, int base, jobject dims); + JPPyBuffer& view, int subs, int base, jobject dims) override; } ; #endif // _JP_VOID_TYPE_H_ \ No newline at end of file diff --git a/native/common/include/jpype.h b/native/common/include/jpype.h index 085df7a27..7930e2fc8 100644 --- a/native/common/include/jpype.h +++ b/native/common/include/jpype.h @@ -100,8 +100,8 @@ extern int PyJPModuleFault_check(uint32_t code); #define JP_FAULT_RETURN(X, Y) if (PyJPModuleFault_check(compile_hash(X))) return Y #define JP_BLOCK(X) if (PyJPModuleFault_check(compile_hash(X))==0) #else -#define JP_FAULT_RETURN(X, Y) if (false) -#define JP_BLOCK(X) if (true) +#define JP_FAULT_RETURN(X, Y) if (false) while (false) +#define JP_BLOCK(X) if (false) while (false) #endif /** Definition of commonly used template types */ @@ -151,14 +151,13 @@ class JPMethodDispatch; class JPField; // Services -class JPTypeFactory; class JPTypeManager; class JPClassLoader; -class JPReferenceQueue; class JPContext; class JPBuffer; class JPPyObject; +extern "C" typedef void (*JCleanupHook)(void*) ; extern "C" struct JPConversionInfo; typedef vector JPClassList; diff --git a/native/common/jp_array.cpp b/native/common/jp_array.cpp index bcb2cb7a7..8b62444c2 100644 --- a/native/common/jp_array.cpp +++ b/native/common/jp_array.cpp @@ -133,7 +133,7 @@ JPPyObject JPArray::getItem(jsize ndx) jarray JPArray::clone(JPJavaFrame& frame, PyObject* obj) { - JPValue value = m_Class->newInstance(frame, m_Length); + JPValue value = m_Class->newArray(frame, m_Length); JPClass* compType = m_Class->getComponentType(); jarray out = (jarray) value.getValue().l; compType->setArrayRange(frame, out, 0, m_Length, 1, obj); diff --git a/native/common/jp_arrayclass.cpp b/native/common/jp_arrayclass.cpp index edb964984..afc557e57 100644 --- a/native/common/jp_arrayclass.cpp +++ b/native/common/jp_arrayclass.cpp @@ -79,7 +79,7 @@ jvalue JPArrayClass::convertToJavaVector(JPJavaFrame& frame, JPPyObjectVector& r JP_TRACE_IN("JPArrayClass::convertToJavaVector"); jsize length = (jsize) (end - start); - jarray array = m_ComponentType->newArrayInstance(frame, length); + jarray array = m_ComponentType->newArrayOf(frame, length); jvalue res; for (jsize i = start; i < end; i++) { @@ -90,9 +90,9 @@ jvalue JPArrayClass::convertToJavaVector(JPJavaFrame& frame, JPPyObjectVector& r JP_TRACE_OUT; } -JPValue JPArrayClass::newInstance(JPJavaFrame& frame, int length) +JPValue JPArrayClass::newArray(JPJavaFrame& frame, int length) { jvalue v; - v.l = m_ComponentType->newArrayInstance(frame, length); + v.l = m_ComponentType->newArrayOf(frame, length); return JPValue(this, v); } diff --git a/native/common/jp_booleantype.cpp b/native/common/jp_booleantype.cpp index 6577f4f6b..17b6b4fbf 100644 --- a/native/common/jp_booleantype.cpp +++ b/native/common/jp_booleantype.cpp @@ -46,7 +46,7 @@ class JPConversionAsBoolean : public JPConversion { public: - virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) + virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) override { if (!PyBool_Check(match.object)) return match.type = JPMatch::_none; @@ -103,7 +103,7 @@ class JPConversionAsBooleanLong : public JPConversionAsBoolean { public: - virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) + virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) override { if (!PyLong_CheckExact(match.object) && !PyIndex_Check(match.object)) @@ -125,7 +125,7 @@ class JPConversionAsBooleanNumber : public JPConversionAsBoolean { public: - virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) + virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) override { if (!PyNumber_Check(match.object)) return match.type = JPMatch::_none; @@ -169,7 +169,7 @@ void JPBooleanType::getConversionInfo(JPConversionInfo &info) PyList_Append(info.ret, (PyObject*) & PyBool_Type); } -jarray JPBooleanType::newArrayInstance(JPJavaFrame& frame, jsize sz) +jarray JPBooleanType::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewBooleanArray(sz); } diff --git a/native/common/jp_bytetype.cpp b/native/common/jp_bytetype.cpp index 4391f3c60..fe4bbf8b2 100644 --- a/native/common/jp_bytetype.cpp +++ b/native/common/jp_bytetype.cpp @@ -101,7 +101,7 @@ void JPByteType::getConversionInfo(JPConversionInfo &info) PyList_Append(info.ret, (PyObject*) m_Context->_int->getHost()); } -jarray JPByteType::newArrayInstance(JPJavaFrame& frame, jsize sz) +jarray JPByteType::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewByteArray(sz); } diff --git a/native/common/jp_chartype.cpp b/native/common/jp_chartype.cpp index 3f53edefc..f9d46f22d 100644 --- a/native/common/jp_chartype.cpp +++ b/native/common/jp_chartype.cpp @@ -95,7 +95,7 @@ class JPConversionAsJChar : public JPConversionJavaValue { public: - JPMatch::Type matches(JPClass *cls, JPMatch &match) override + virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JPValue *value = match.getJavaSlot(); if (value == NULL) @@ -112,7 +112,7 @@ class JPConversionAsJChar : public JPConversionJavaValue return JPMatch::_implicit; // stop search } - void getInfo(JPClass *cls, JPConversionInfo &info) + virtual void getInfo(JPClass *cls, JPConversionInfo &info) override { JPContext *context = cls->getContext(); PyList_Append(info.exact, (PyObject*) context->_char->getHost()); @@ -144,7 +144,7 @@ void JPCharType::getConversionInfo(JPConversionInfo &info) PyList_Append(info.ret, (PyObject*) m_Context->_char->getHost()); } -jarray JPCharType::newArrayInstance(JPJavaFrame& frame, jsize sz) +jarray JPCharType::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewCharArray(sz); } diff --git a/native/common/jp_class.cpp b/native/common/jp_class.cpp index 9e4355188..d74807bac 100644 --- a/native/common/jp_class.cpp +++ b/native/common/jp_class.cpp @@ -125,7 +125,7 @@ JPClass* JPClass::newArrayType(JPJavaFrame &frame, long d) return frame.findClassByName(ss.str()); } -jarray JPClass::newArrayInstance(JPJavaFrame& frame, jsize sz) +jarray JPClass::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewObjectArray(sz, getJavaClass(), NULL); } diff --git a/native/common/jp_classhints.cpp b/native/common/jp_classhints.cpp index 5cf360ad3..50c663640 100644 --- a/native/common/jp_classhints.cpp +++ b/native/common/jp_classhints.cpp @@ -206,7 +206,7 @@ class JPAttributeConversion : public JPPythonConversion JP_TRACE_OUT; } - virtual void getInfo(JPClass *cls, JPConversionInfo &info) + virtual void getInfo(JPClass *cls, JPConversionInfo &info) override { PyList_Append(info.attributes, JPPyString::fromStringUTF8(attribute_).get()); } @@ -332,7 +332,7 @@ class JPHintsConversion : public JPConversion { public: - virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) + virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) override { PyJPClassHints *pyhints = (PyJPClassHints*) cls->getHints(); // GCOVR_EXCL_START @@ -359,7 +359,7 @@ class JPHintsConversion : public JPConversion hints->getInfo(cls, info); } - virtual jvalue convert(JPMatch &match) + virtual jvalue convert(JPMatch &match) override { return jvalue(); } @@ -496,7 +496,7 @@ class JPConversionBuffer : public JPConversion JPArrayClass *acls = (JPArrayClass *) match.closure; jsize length = (jsize) PySequence_Length(match.object); JPClass *ccls = acls->getComponentType(); - jarray array = ccls->newArrayInstance(frame, (jsize) length); + jarray array = ccls->newArrayOf(frame, (jsize) length); ccls->setArrayRange(frame, array, 0, length, 1, match.object); res.l = frame.keep(array); return res; @@ -552,7 +552,7 @@ class JPConversionSequence : public JPConversion JPArrayClass *acls = (JPArrayClass *) match.closure; jsize length = (jsize) PySequence_Length(match.object); JPClass *ccls = acls->getComponentType(); - jarray array = ccls->newArrayInstance(frame, (jsize) length); + jarray array = ccls->newArrayOf(frame, (jsize) length); ccls->setArrayRange(frame, array, 0, length, 1, match.object); res.l = frame.keep(array); return res; @@ -781,7 +781,7 @@ class JPConversionBoxLong : public JPConversionBox PyList_Append(info.implicit, proto.get()); } - jvalue convert(JPMatch &match) override + jvalue convert(JPMatch &match) override { PyTypeObject* type = Py_TYPE(match.object); JPJavaFrame *frame = match.frame; diff --git a/native/common/jp_classloader.cpp b/native/common/jp_classloader.cpp index 638337bac..f9060d6b7 100644 --- a/native/common/jp_classloader.cpp +++ b/native/common/jp_classloader.cpp @@ -15,53 +15,105 @@ *****************************************************************************/ #include #include +#include #include -#include jobject JPClassLoader::getBootLoader() { return m_BootLoader.get(); } +static jobject toURL(JPJavaFrame &frame, const string& path) +{ + // file = new File("org.jpype.jar"); + jclass fileClass = frame.FindClass("java/io/File"); + jmethodID newFile = frame.GetMethodID(fileClass, "", "(Ljava/lang/String;)V"); + jvalue v[3]; + v[0].l = frame.NewStringUTF(path.c_str()); + jobject file = frame.NewObjectA(fileClass, newFile, v); + + // url = file.toURI().toURL(); + jmethodID toURI = frame.GetMethodID(fileClass, "toURI", "()Ljava/net/URI;"); + jobject uri = frame.CallObjectMethodA(file, toURI, NULL); + jclass uriClass = frame.GetObjectClass(uri); + jmethodID toURL = frame.GetMethodID(uriClass, "toURL", "()Ljava/net/URL;"); + return frame.CallObjectMethodA(uri, toURL, NULL); +} + JPClassLoader::JPClassLoader(JPJavaFrame& frame) { JP_TRACE_IN("JPClassLoader::JPClassLoader"); m_Context = frame.getContext(); // Define the class loader - jclass classLoaderClass = (jclass) frame.FindClass("java/lang/ClassLoader"); + m_ClassClass = JPClassRef(frame, frame.FindClass("java/lang/Class")); + m_ForNameID = frame.GetStaticMethodID(m_ClassClass.get(), "forName", + "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"); + jclass classLoaderClass = frame.FindClass("java/lang/ClassLoader"); jmethodID getSystemClassLoader = frame.GetStaticMethodID(classLoaderClass, "getSystemClassLoader", "()Ljava/lang/ClassLoader;"); m_SystemClassLoader = JPObjectRef(frame, frame.CallStaticObjectMethodA(classLoaderClass, getSystemClassLoader, 0)); - // Set up the loader - m_FindClass = frame.GetMethodID(classLoaderClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); - jclass cls = frame.DefineClass("org/jpype/classloader/JPypeClassLoader", m_SystemClassLoader.get(), - JPThunk::_org_jpype_classloader_JPypeClassLoader, - JPThunk::_org_jpype_classloader_JPypeClassLoader_size); + jclass dynamicLoaderClass = frame.getEnv()->FindClass("org/jpype/classloader/DynamicClassLoader"); + if (dynamicLoaderClass != NULL) + { + // Easy the Dynamic loader is already in the path, so just use it as the bootloader + jmethodID newDyLoader = frame.GetMethodID(dynamicLoaderClass, "", + "(Ljava/lang/ClassLoader;)V"); + jvalue v; + v.l = m_SystemClassLoader.get(); + m_BootLoader = JPObjectRef(frame, frame.NewObjectA(dynamicLoaderClass, newDyLoader, &v)); + return; + } + frame.ExceptionClear(); + + // Harder, we need to find the _jpype module and use __file__ to obtain a + // path. + JPPyObject pypath = JPPyObject::call(PyObject_GetAttrString(PyJPModule, "__file__")); + string path = JPPyString::asStringUTF8(pypath.get()); + string::size_type i = path.find_last_of('\\'); + if (i == string::npos) + i = path.find_last_of('/'); + if (i == string::npos) + JP_RAISE(PyExc_RuntimeError, "Can't find jar path"); + path = path.substr(0, i + 1); + jobject url1 = toURL(frame, path + "org.jpype.jar"); + // jobject url2 = toURL(frame, path + "lib/asm-8.0.1.jar"); + + // urlArray = new URL[]{url}; + jclass urlClass = frame.GetObjectClass(url1); + jobjectArray urlArray = frame.NewObjectArray(1, urlClass, NULL); + frame.SetObjectArrayElement(urlArray, 0, url1); + // frame.SetObjectArrayElement(urlArray, 1, url2); - // Set up class loader - frame.GetMethodID(cls, "", "(Ljava/lang/ClassLoader;)V"); + // cl = new URLClassLoader(urlArray); + jclass urlLoaderClass = frame.FindClass("java/net/URLClassLoader"); + jmethodID newURLClassLoader = frame.GetMethodID(urlLoaderClass, "", "([Ljava/net/URL;Ljava/lang/ClassLoader;)V"); + jvalue v[3]; + v[0].l = (jobject) urlArray; + v[1].l = (jobject) m_SystemClassLoader.get(); + jobject cl = frame.NewObjectA(urlLoaderClass, newURLClassLoader, v); - jmethodID getInstanceID = frame.GetStaticMethodID(cls, "getInstance", "()Lorg/jpype/classloader/JPypeClassLoader;"); - m_BootLoader = JPObjectRef(frame, frame.NewGlobalRef( - frame.CallStaticObjectMethodA(cls, getInstanceID, 0))); + // Class dycl = Class.forName("org.jpype.classloader.DynamicClassLoader", true, cl); + v[0].l = frame.NewStringUTF("org.jpype.classloader.DynamicClassLoader"); + v[1].z = true; + v[2].l = cl; + jclass dyClass = (jclass) frame.CallStaticObjectMethodA(m_ClassClass.get(), m_ForNameID, v); - // Load the jar - jbyteArray jar = frame.NewByteArray(JPThunk::_org_jpype_size); - frame.SetByteArrayRegion(jar, 0, JPThunk::_org_jpype_size, JPThunk::_org_jpype); + // dycl.newInstance(systemClassLoader); + jmethodID newDyLoader = frame.GetMethodID(dyClass, "", "(Ljava/lang/ClassLoader;)V"); + v[0].l = cl; + m_BootLoader = JPObjectRef(frame, frame.NewObjectA(dyClass, newDyLoader, v)); - jvalue v; - v.l = jar; - jmethodID importJarID = frame.GetMethodID(cls, "importJar", "([B)V"); - frame.CallVoidMethodA(m_BootLoader.get(), importJarID, &v); JP_TRACE_OUT; // GCOVR_EXCL_LINE } jclass JPClassLoader::findClass(JPJavaFrame& frame, string name) { - jvalue v; - v.l = frame.NewStringUTF(name.c_str()); - return (jclass) frame.CallObjectMethodA(m_BootLoader.get(), m_FindClass, &v); + jvalue v[3]; + v[0].l = frame.NewStringUTF(name.c_str()); + v[1].z = true; + v[2].l = m_BootLoader.get(); + return (jclass) frame.CallStaticObjectMethodA(m_ClassClass.get(), m_ForNameID, v); } diff --git a/native/common/jp_context.cpp b/native/common/jp_context.cpp index 790bc99e8..49c08ac13 100644 --- a/native/common/jp_context.cpp +++ b/native/common/jp_context.cpp @@ -28,7 +28,6 @@ #include "jp_longtype.h" #include "jp_floattype.h" #include "jp_doubletype.h" -#include "jp_reference_queue.h" #include "jp_proxy.h" #include "jp_platform.h" #include "jp_gc.h" @@ -74,11 +73,10 @@ JPContext::JPContext() _java_lang_reflect_Method = 0; _java_lang_reflect_Field = 0; + _java_nio_ByteBuffer = 0; - m_TypeFactory = 0; m_TypeManager = 0; m_ClassLoader = 0; - m_ReferenceQueue = 0; m_Object_ToStringID = 0; m_Object_EqualsID = 0; @@ -110,15 +108,14 @@ JPContext::JPContext() m_FloatValueID = NULL; m_DoubleValueID = NULL; m_Context_GetStackFrameID = NULL; + m_Embedded = false; m_GC = new JPGarbageCollection(this); } JPContext::~JPContext() { - delete m_TypeFactory; delete m_TypeManager; - delete m_ReferenceQueue; delete m_GC; } @@ -165,11 +162,6 @@ void JPContext::loadEntryPoints(const string& path) JP_TRACE_OUT; } -static void interruptPy() -{ - PyErr_SetInterrupt(); -} - void JPContext::startJVM(const string& vmPath, const StringVector& args, bool ignoreUnrecognized, bool convertStrings) { @@ -228,166 +220,162 @@ void JPContext::startJVM(const string& vmPath, const StringVector& args, JP_RAISE(PyExc_RuntimeError, "Unable to start JVM"); } - // Connect our resources to the JVM + initializeResources(env); + JP_TRACE_OUT; +} + +void JPContext::attachJVM(JNIEnv* env) +{ + env->GetJavaVM(&m_JavaVM); + m_Embedded = true; + initializeResources(env); +} + +void JPContext::initializeResources(JNIEnv* env) +{ + JPJavaFrame frame = JPJavaFrame::external(this, env); + // This is the only frame that we can use until the system + // is initialized. Any other frame creation will result in an error. + + jclass throwableClass = (jclass) frame.FindClass("java/lang/Throwable"); + m_Throwable_GetCauseID = frame.GetMethodID(throwableClass, "getCause", "()Ljava/lang/Throwable;"); + m_Throwable_GetMessageID = frame.GetMethodID(throwableClass, "getMessage", "()Ljava/lang/String;"); + + // After the JVM is created but before the context is started, we need + // to set up all the services that the context will need. + JP_TRACE("Initialize"); + + // We need these first because if anything goes south this is the first + // thing that will get hit. + jclass objectClass = frame.FindClass("java/lang/Object"); + m_Object_ToStringID = frame.GetMethodID(objectClass, "toString", "()Ljava/lang/String;"); + m_Object_EqualsID = frame.GetMethodID(objectClass, "equals", "(Ljava/lang/Object;)Z"); + m_Object_HashCodeID = frame.GetMethodID(objectClass, "hashCode", "()I"); + m_Object_GetClassID = frame.GetMethodID(objectClass, "getClass", "()Ljava/lang/Class;"); + + m_NoSuchMethodError = JPClassRef(frame, (jclass) frame.FindClass("java/lang/NoSuchMethodError")); + m_RuntimeException = JPClassRef(frame, (jclass) frame.FindClass("java/lang/RuntimeException")); + + jclass stringClass = frame.FindClass("java/lang/String"); + m_String_ToCharArrayID = frame.GetMethodID(stringClass, "toCharArray", "()[C"); + + jclass classClass = frame.FindClass("java/lang/Class"); + m_Class_GetNameID = frame.GetMethodID(classClass, "getName", "()Ljava/lang/String;"); + + // Bootloader needs to go first so we can load classes + m_ClassLoader = new JPClassLoader(frame); + + JP_TRACE("Install native"); + // Start the rest of the services + m_TypeManager = new JPTypeManager(frame); + + // Prepare to launch + JP_TRACE("Start Context"); + jclass contextClass = m_ClassLoader->findClass(frame, "org.jpype.JPypeContext"); + m_Context_GetStackFrameID = frame.GetMethodID(contextClass, "getStackTrace", + "(Ljava/lang/Throwable;Ljava/lang/Throwable;)[Ljava/lang/Object;"); + + jmethodID startMethod = frame.GetStaticMethodID(contextClass, "createContext", + "(JLjava/lang/ClassLoader;Ljava/lang/String;)Lorg/jpype/JPypeContext;"); + + // Find the native library + JPPyObject import = JPPyObject::call(PyImport_AddModule("importlib.util")); + JPPyObject jpype = JPPyObject::call(PyObject_CallMethod(import.get(), "find_spec", "s", "_jpype")); + JPPyObject origin = JPPyObject::call(PyObject_GetAttrString(jpype.get(), "origin")); + + // Launch + jvalue val[3]; + val[0].j = (jlong) this; + val[1].l = m_ClassLoader->getBootLoader(); + val[2].l = 0; + + if (!m_Embedded) { - // This is the only frame that we can use until the system - // is initialized. Any other frame creation will result in an error. - JPJavaFrame frame = JPJavaFrame::external(this, env); - - jclass throwableClass = (jclass) frame.FindClass("java/lang/Throwable"); - m_Throwable_GetCauseID = frame.GetMethodID(throwableClass, "getCause", "()Ljava/lang/Throwable;"); - m_Throwable_GetMessageID = frame.GetMethodID(throwableClass, "getMessage", "()Ljava/lang/String;"); - - // After the JVM is created but before the context is started, we need - // to set up all the services that the context will need. - JP_TRACE("Initialize"); - - // We need these first because if anything goes south this is the first - // thing that will get hit. - jclass objectClass = frame.FindClass("java/lang/Object"); - m_Object_ToStringID = frame.GetMethodID(objectClass, "toString", "()Ljava/lang/String;"); - m_Object_EqualsID = frame.GetMethodID(objectClass, "equals", "(Ljava/lang/Object;)Z"); - m_Object_HashCodeID = frame.GetMethodID(objectClass, "hashCode", "()I"); - m_Object_GetClassID = frame.GetMethodID(objectClass, "getClass", "()Ljava/lang/Class;"); - - m_NoSuchMethodError = JPClassRef(frame, (jclass) frame.FindClass("java/lang/NoSuchMethodError")); - m_RuntimeException = JPClassRef(frame, (jclass) frame.FindClass("java/lang/RuntimeException")); - - jclass stringClass = frame.FindClass("java/lang/String"); - m_String_ToCharArrayID = frame.GetMethodID(stringClass, "toCharArray", "()[C"); - - jclass classClass = frame.FindClass("java/lang/Class"); - m_Class_GetNameID = frame.GetMethodID(classClass, "getName", "()Ljava/lang/String;"); - - // Bootloader needs to go first so we can load classes - m_ClassLoader = new JPClassLoader(frame); - - JP_TRACE("Install native"); - // Start the rest of the services - m_TypeFactory = new JPTypeFactory(frame); - m_TypeManager = new JPTypeManager(frame); - m_ReferenceQueue = new JPReferenceQueue(frame); - - // Prepare to launch - JP_TRACE("Start Context"); - jclass contextClass = m_ClassLoader->findClass(frame, "org.jpype.JPypeContext"); - m_Context_GetStackFrameID = frame.GetMethodID(contextClass, "getStackTrace", - "(Ljava/lang/Throwable;Ljava/lang/Throwable;)[Ljava/lang/Object;"); - - jmethodID startMethod = frame.GetStaticMethodID(contextClass, "createContext", - "(JLjava/lang/ClassLoader;)Lorg/jpype/JPypeContext;"); - - JNINativeMethod method[1]; - method[0].name = (char*) "onShutdown"; - method[0].signature = (char*) "(J)V"; - method[0].fnPtr = (void*) JPContext::onShutdown; - frame.GetMethodID(contextClass, "", "()V"); - frame.RegisterNatives(contextClass, method, 1); - - // Launch - jvalue val[2]; - val[0].j = (jlong) this; - val[1].l = m_ClassLoader->getBootLoader(); - m_JavaContext = JPObjectRef(frame, frame.CallStaticObjectMethodA(contextClass, startMethod, val)); - - // Post launch - JP_TRACE("Connect resources"); - // Hook up the type manager - jmethodID getTypeManager = frame.GetMethodID(contextClass, "getTypeManager", - "()Lorg/jpype/manager/TypeManager;"); - m_TypeManager->m_JavaTypeManager = JPObjectRef(frame, - frame.CallObjectMethodA(m_JavaContext.get(), getTypeManager, 0)); - - // Hook up the reference queue - jmethodID getReferenceQueue = frame.GetMethodID(contextClass, "getReferenceQueue", - "()Lorg/jpype/ref/JPypeReferenceQueue;"); - m_ReferenceQueue->m_ReferenceQueue = JPObjectRef(frame, - frame.CallObjectMethodA(m_JavaContext.get(), getReferenceQueue, 0)); - - // Set up methods after everything is start so we get better error - // messages - m_CallMethodID = frame.GetMethodID(contextClass, "callMethod", - "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"); - m_Context_collectRectangularID = frame.GetMethodID(contextClass, - "collectRectangular", - "(Ljava/lang/Object;)[Ljava/lang/Object;"); - - m_Context_assembleID = frame.GetMethodID(contextClass, - "assemble", - "([ILjava/lang/Object;)Ljava/lang/Object;"); - - m_Context_GetFunctionalID = frame.GetMethodID(contextClass, - "getFunctional", - "(Ljava/lang/Class;)Ljava/lang/String;"); - - m_Context_CreateExceptionID = frame.GetMethodID(contextClass, "createException", - "(JJ)Ljava/lang/Exception;"); - m_Context_GetExcClassID = frame.GetMethodID(contextClass, "getExcClass", - "(Ljava/lang/Throwable;)J"); - m_Context_GetExcValueID = frame.GetMethodID(contextClass, "getExcValue", - "(Ljava/lang/Throwable;)J"); - m_Context_OrderID = frame.GetMethodID(contextClass, "order", "(Ljava/nio/Buffer;)Z"); - m_Context_IsPackageID = frame.GetMethodID(contextClass, "isPackage", "(Ljava/lang/String;)Z"); - m_Context_GetPackageID = frame.GetMethodID(contextClass, "getPackage", "(Ljava/lang/String;)Lorg/jpype/pkg/JPypePackage;"); - - jclass packageClass = m_ClassLoader->findClass(frame, "org.jpype.pkg.JPypePackage"); - m_Package_GetObjectID = frame.GetMethodID(packageClass, "getObject", - "(Ljava/lang/String;)Ljava/lang/Object;"); - m_Package_GetContentsID = frame.GetMethodID(packageClass, "getContents", - "()[Ljava/lang/String;"); - m_Context_NewWrapperID = frame.GetMethodID(contextClass, "newWrapper", - "(J)V"); - - m_Array = JPClassRef(frame, frame.FindClass("java/lang/reflect/Array")); - m_Array_NewInstanceID = frame.GetStaticMethodID(m_Array.get(), "newInstance", - "(Ljava/lang/Class;[I)Ljava/lang/Object;"); - - jclass bufferClass = frame.FindClass("java/nio/Buffer"); - m_Buffer_IsReadOnlyID = frame.GetMethodID(bufferClass, "isReadOnly", - "()Z"); - - jclass comparableClass = frame.FindClass("java/lang/Comparable"); - m_CompareToID = frame.GetMethodID(comparableClass, "compareTo", - "(Ljava/lang/Object;)I"); - - jclass signalClass = getClassLoader()->findClass(frame, "org.jpype.JPypeSignal"); - - method[0].name = (char*) "interruptPy"; - method[0].signature = (char*) "()V"; - method[0].fnPtr = (void*) interruptPy; - frame.GetMethodID(signalClass, "", "()V"); - frame.RegisterNatives(signalClass, method, 1); - - jclass proxyClass = getClassLoader()->findClass(frame, "org.jpype.proxy.JPypeProxy"); - - method[0].name = (char*) "hostInvoke"; - method[0].signature = (char*) "(JLjava/lang/String;JJ[J[Ljava/lang/Object;)Ljava/lang/Object;"; - method[0].fnPtr = (void*) &JPProxy::hostInvoke; - frame.GetMethodID(proxyClass, "", "()V"); - frame.RegisterNatives(proxyClass, method, 1); - - m_ProxyClass = JPClassRef(frame, proxyClass); - m_Proxy_NewID = frame.GetStaticMethodID(m_ProxyClass.get(), - "newProxy", - "(Lorg/jpype/JPypeContext;JJ[Ljava/lang/Class;)Lorg/jpype/proxy/JPypeProxy;"); - m_Proxy_NewInstanceID = frame.GetMethodID(m_ProxyClass.get(), - "newInstance", - "()Ljava/lang/Object;"); - - m_GC->init(frame); - - // Testing code to make sure C++ exceptions are handled. - // FIXME find a way to call this from instrumentation. - // throw std::runtime_error("Failed"); - // Everything is started. + PyObject *import = PyImport_AddModule("importlib.util"); + JPPyObject jpype = JPPyObject::call(PyObject_CallMethod(import, "find_spec", "s", "_jpype")); + JPPyObject origin = JPPyObject::call(PyObject_GetAttrString(jpype.get(), "origin")); + val[2].l = frame.fromStringUTF8(JPPyString::asStringUTF8(origin.get())); } + m_JavaContext = JPObjectRef(frame, frame.CallStaticObjectMethodA(contextClass, startMethod, val)); + + // Post launch + JP_TRACE("Connect resources"); + // Hook up the type manager + jmethodID getTypeManager = frame.GetMethodID(contextClass, "getTypeManager", + "()Lorg/jpype/manager/TypeManager;"); + m_TypeManager->m_JavaTypeManager = JPObjectRef(frame, + frame.CallObjectMethodA(m_JavaContext.get(), getTypeManager, 0)); + + // Set up methods after everything is start so we get better error + // messages + m_CallMethodID = frame.GetMethodID(contextClass, "callMethod", + "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"); + m_Context_collectRectangularID = frame.GetMethodID(contextClass, + "collectRectangular", + "(Ljava/lang/Object;)[Ljava/lang/Object;"); + + m_Context_assembleID = frame.GetMethodID(contextClass, + "assemble", + "([ILjava/lang/Object;)Ljava/lang/Object;"); + + m_Context_GetFunctionalID = frame.GetMethodID(contextClass, + "getFunctional", + "(Ljava/lang/Class;)Ljava/lang/String;"); + + m_Context_CreateExceptionID = frame.GetMethodID(contextClass, "createException", + "(JJ)Ljava/lang/Exception;"); + m_Context_GetExcClassID = frame.GetMethodID(contextClass, "getExcClass", + "(Ljava/lang/Throwable;)J"); + m_Context_GetExcValueID = frame.GetMethodID(contextClass, "getExcValue", + "(Ljava/lang/Throwable;)J"); + m_Context_OrderID = frame.GetMethodID(contextClass, "order", "(Ljava/nio/Buffer;)Z"); + m_Context_IsPackageID = frame.GetMethodID(contextClass, "isPackage", "(Ljava/lang/String;)Z"); + m_Context_GetPackageID = frame.GetMethodID(contextClass, "getPackage", "(Ljava/lang/String;)Lorg/jpype/pkg/JPypePackage;"); + + jclass packageClass = m_ClassLoader->findClass(frame, "org.jpype.pkg.JPypePackage"); + m_Package_GetObjectID = frame.GetMethodID(packageClass, "getObject", + "(Ljava/lang/String;)Ljava/lang/Object;"); + m_Package_GetContentsID = frame.GetMethodID(packageClass, "getContents", + "()[Ljava/lang/String;"); + m_Context_NewWrapperID = frame.GetMethodID(contextClass, "newWrapper", + "(J)V"); + + m_Array = JPClassRef(frame, frame.FindClass("java/lang/reflect/Array")); + m_Array_NewInstanceID = frame.GetStaticMethodID(m_Array.get(), "newInstance", + "(Ljava/lang/Class;[I)Ljava/lang/Object;"); + + jclass bufferClass = frame.FindClass("java/nio/Buffer"); + m_Buffer_IsReadOnlyID = frame.GetMethodID(bufferClass, "isReadOnly", + "()Z"); + + jclass comparableClass = frame.FindClass("java/lang/Comparable"); + m_CompareToID = frame.GetMethodID(comparableClass, "compareTo", + "(Ljava/lang/Object;)I"); + + jclass proxyClass = getClassLoader()->findClass(frame, "org.jpype.proxy.JPypeProxy"); + + m_ProxyClass = JPClassRef(frame, proxyClass); + m_Proxy_NewID = frame.GetStaticMethodID(m_ProxyClass.get(), + "newProxy", + "(Lorg/jpype/JPypeContext;JJ[Ljava/lang/Class;)Lorg/jpype/proxy/JPypeProxy;"); + m_Proxy_NewInstanceID = frame.GetMethodID(m_ProxyClass.get(), + "newInstance", + "()Ljava/lang/Object;"); + + m_GC->init(frame); + + _java_nio_ByteBuffer = this->getTypeManager()->findClassByName("java.nio.ByteBuffer"); + + // Testing code to make sure C++ exceptions are handled. + // FIXME find a way to call this from instrumentation. + // throw std::runtime_error("Failed"); + // Everything is started. m_Running = true; - JP_TRACE_OUT; } -JNIEXPORT void JNICALL JPContext::onShutdown(JNIEnv *env, jobject obj, jlong contextPtr) +void JPContext::onShutdown() { - ((JPContext*) contextPtr)->m_Running = false; + m_Running = false; } void JPContext::shutdownJVM() @@ -395,6 +383,8 @@ void JPContext::shutdownJVM() JP_TRACE_IN("JPContext::shutdown"); if (m_JavaVM == NULL) JP_RAISE(PyExc_RuntimeError, "Attempt to shutdown without a live JVM"); + // if (m_Embedded) + // JP_RAISE(PyExc_RuntimeError, "Cannot shutdown from embedded Python"); // Wait for all non-demon threads to terminate JP_TRACE("Destroy JVM"); @@ -479,3 +469,15 @@ JNIEnv* JPContext::getEnv() } return env; } + +extern "C" JNIEXPORT void JNICALL Java_org_jpype_JPypeContext_onShutdown +(JNIEnv *env, jobject obj, jlong contextPtr) +{ + ((JPContext*) contextPtr)->onShutdown(); +} + +extern "C" JNIEXPORT void JNICALL Java_org_jpype_JPypeSignal_interruptPy +(JNIEnv *env, jclass cls) +{ + PyErr_SetInterrupt(); +} diff --git a/native/common/jp_doubletype.cpp b/native/common/jp_doubletype.cpp index 96b428e54..984de000b 100644 --- a/native/common/jp_doubletype.cpp +++ b/native/common/jp_doubletype.cpp @@ -106,7 +106,7 @@ class JPConversionAsJDouble : public JPConversionJavaValue } - void getInfo(JPClass *cls, JPConversionInfo &info) + virtual void getInfo(JPClass *cls, JPConversionInfo &info) override { JPContext *context = cls->getContext(); PyList_Append(info.exact, (PyObject*) context->_double->getHost()); @@ -147,7 +147,7 @@ void JPDoubleType::getConversionInfo(JPConversionInfo &info) PyList_Append(info.ret, PyJPClass_create(frame, this).get()); } -jarray JPDoubleType::newArrayInstance(JPJavaFrame& frame, jsize sz) +jarray JPDoubleType::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewDoubleArray(sz); } diff --git a/native/common/jp_exception.cpp b/native/common/jp_exception.cpp index 071156f48..372b3d350 100644 --- a/native/common/jp_exception.cpp +++ b/native/common/jp_exception.cpp @@ -255,16 +255,21 @@ void JPypeException::convertPythonToJava(JPContext* context) } } + if (context->m_Context_CreateExceptionID == NULL) + { + frame.ThrowNew(frame.FindClass("java/lang/RuntimeException"), getMessage().c_str()); + return; + } + + // Otherwise jvalue v[2]; v[0].j = (jlong) eframe.m_ExceptionClass.get(); v[1].j = (jlong) eframe.m_ExceptionValue.get(); th = (jthrowable) frame.CallObjectMethodA(context->getJavaContext(), context->m_Context_CreateExceptionID, v); - context->getReferenceQueue()->registerRef((jobject) th, - eframe.m_ExceptionClass.get()); - context->getReferenceQueue()->registerRef((jobject) th, - eframe.m_ExceptionValue.get()); + frame.registerRef((jobject) th, eframe.m_ExceptionClass.get()); + frame.registerRef((jobject) th, eframe.m_ExceptionValue.get()); eframe.clear(); frame.Throw(th); JP_TRACE_OUT; // GCOVR_EXCL_LINE diff --git a/native/common/jp_floattype.cpp b/native/common/jp_floattype.cpp index 6af9df680..d52d82044 100644 --- a/native/common/jp_floattype.cpp +++ b/native/common/jp_floattype.cpp @@ -90,7 +90,7 @@ class JPConversionAsJFloat : public JPConversionJavaValue return JPMatch::_implicit; // stop search } - void getInfo(JPClass *cls, JPConversionInfo &info) + virtual void getInfo(JPClass *cls, JPConversionInfo &info) override { JPContext *context = cls->getContext(); PyList_Append(info.exact, (PyObject*) context->_float->getHost()); @@ -129,7 +129,7 @@ void JPFloatType::getConversionInfo(JPConversionInfo &info) PyList_Append(info.ret, (PyObject*) m_Context->_float->getHost()); } -jarray JPFloatType::newArrayInstance(JPJavaFrame& frame, jsize sz) +jarray JPFloatType::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewFloatArray(sz); } diff --git a/native/common/jp_inttype.cpp b/native/common/jp_inttype.cpp index e7ee7681e..ec6572add 100644 --- a/native/common/jp_inttype.cpp +++ b/native/common/jp_inttype.cpp @@ -55,7 +55,7 @@ class JPConversionJInt : public JPConversionJavaValue { public: - virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) + virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JPValue *value = match.getJavaSlot(); if (value == NULL) @@ -89,7 +89,7 @@ class JPConversionJInt : public JPConversionJavaValue return JPMatch::_implicit; //short cut further checks } - void getInfo(JPClass *cls, JPConversionInfo &info) + virtual void getInfo(JPClass *cls, JPConversionInfo &info) override { JPContext *context = cls->getContext(); PyList_Append(info.exact, (PyObject*) context->_int->getHost()); @@ -126,7 +126,7 @@ void JPIntType::getConversionInfo(JPConversionInfo &info) PyList_Append(info.ret, (PyObject*) m_Context->_int->getHost()); } -jarray JPIntType::newArrayInstance(JPJavaFrame& frame, jsize sz) +jarray JPIntType::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewIntArray(sz); } diff --git a/native/common/jp_javaframe.cpp b/native/common/jp_javaframe.cpp index eb570169f..aae6e3ba1 100644 --- a/native/common/jp_javaframe.cpp +++ b/native/common/jp_javaframe.cpp @@ -15,6 +15,7 @@ *****************************************************************************/ #include #include "jpype.h" +#include "jp_reference_queue.h" /*****************************************************************************/ // Local frames represent the JNIEnv for memory handling all java @@ -30,7 +31,7 @@ static void jpype_frame_check(int popped) } #define JP_FRAME_CHECK() jpype_frame_check(m_Popped) #else -#define JP_FRAME_CHECK() if (true) +#define JP_FRAME_CHECK() if (false) while (false) #endif JPJavaFrame::JPJavaFrame(JPContext* context, JNIEnv* p_env, int size, bool outer) @@ -575,6 +576,12 @@ jboolean JPJavaFrame::CallNonvirtualBooleanMethodA(jobject obj, jclass claz, jme m_Env->CallNonvirtualBooleanMethodA(obj, claz, mid, val)); } +jclass JPJavaFrame::GetObjectClass(jobject obj) +{ + JAVA_RETURN_OBJ(jclass, "JPJavaFrame::GetObjectClass", + m_Env->GetObjectClass(obj)); +} + jobject JPJavaFrame::GetStaticObjectField(jclass clazz, jfieldID fid) { JAVA_RETURN_OBJ(jobject, "JPJavaFrame::GetStaticObjectField", @@ -1271,3 +1278,13 @@ void JPJavaFrame::newWrapper(JPClass* cls) CallVoidMethodA(m_Context->getJavaContext(), m_Context->m_Context_NewWrapperID, &jv); } + +void JPJavaFrame::registerRef(jobject obj, PyObject* hostRef) +{ + JPReferenceQueue::registerRef(*this, obj, hostRef); +} + +void JPJavaFrame::registerRef(jobject obj, void* ref, JCleanupHook cleanup) +{ + JPReferenceQueue::registerRef(*this, obj, ref, cleanup); +} diff --git a/native/common/jp_longtype.cpp b/native/common/jp_longtype.cpp index 5a4c29d36..aa1ed1ab1 100644 --- a/native/common/jp_longtype.cpp +++ b/native/common/jp_longtype.cpp @@ -126,7 +126,7 @@ void JPLongType::getConversionInfo(JPConversionInfo &info) PyList_Append(info.ret, (PyObject*) m_Context->_long->getHost()); } -jarray JPLongType::newArrayInstance(JPJavaFrame& frame, jsize sz) +jarray JPLongType::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewLongArray(sz); } diff --git a/native/common/jp_proxy.cpp b/native/common/jp_proxy.cpp index 0e639ec0b..c65ba657d 100644 --- a/native/common/jp_proxy.cpp +++ b/native/common/jp_proxy.cpp @@ -46,7 +46,7 @@ JPPyObject getArgs(JPContext* context, jlongArray parameterTypePtrs, JP_TRACE_OUT; } -JNIEXPORT jobject JNICALL JPProxy::hostInvoke( +extern "C" JNIEXPORT jobject JNICALL Java_org_jpype_proxy_JPypeProxy_hostInvoke( JNIEnv *env, jclass clazz, jlong contextPtr, jstring name, jlong hostObj, @@ -80,7 +80,6 @@ JNIEXPORT jobject JNICALL JPProxy::hostInvoke( // Get the callable object JPPyObject callable(((JPProxy*) hostObj)->getCallable(cname)); - PyErr_Clear(); // If method can't be called, throw an exception if (callable.isNull() || callable.get() == Py_None) @@ -306,4 +305,4 @@ JPPyObject JPProxyFunctional::getCallable(const string& cname) if (cname == m_Functional->getMethod()) return JPPyObject::accept(PyObject_GetAttrString(m_Instance->m_Target, "__call__")); return JPPyObject::accept(PyObject_GetAttrString((PyObject*) m_Instance, cname.c_str())); -} \ No newline at end of file +} diff --git a/native/common/jp_reference_queue.cpp b/native/common/jp_reference_queue.cpp index 8a2572d3d..88b0e818d 100644 --- a/native/common/jp_reference_queue.cpp +++ b/native/common/jp_reference_queue.cpp @@ -13,11 +13,16 @@ See NOTICE file for details. *****************************************************************************/ +#include #include #include "jpype.h" #include "jp_classloader.h" #include "jp_reference_queue.h" #include "jp_gc.h" +#include "pyjp.h" + +static jobject s_ReferenceQueue = NULL; +static jmethodID s_ReferenceQueueRegisterMethod = NULL; extern "C" { @@ -27,15 +32,25 @@ static void releasePython(void* host) Py_XDECREF((PyObject*) host); } +/* + * Class: org_jpype_ref_JPypeReferenceQueue + * Method: init + * Signature: (Ljava/lang/Object;Ljava/lang/reflect/Method;)V + */ +JNIEXPORT void JNICALL Java_org_jpype_ref_JPypeReferenceNative_init +(JNIEnv *env, jclass clazz, jobject refqueue, jobject registerID) +{ + s_ReferenceQueue = env->NewGlobalRef(refqueue); + s_ReferenceQueueRegisterMethod = env->FromReflectedMethod(registerID); } -JNIEXPORT void JNICALL Java_jpype_ref_JPypeReferenceQueue_removeHostReference( - JNIEnv *env, jclass clazz, jlong contextPtr, jlong host, jlong cleanup) +JNIEXPORT void JNICALL Java_org_jpype_ref_JPypeReferenceNative_removeHostReference +(JNIEnv *env, jclass, jlong host, jlong cleanup) { + JPContext* context = JPContext_global; // Exceptions are not allowed here try { - JPContext *context = (JPContext*) contextPtr; JPJavaFrame frame = JPJavaFrame::external((JPContext*) context, env); JPPyCallAcquire callback; if (cleanup != 0) @@ -50,64 +65,31 @@ JNIEXPORT void JNICALL Java_jpype_ref_JPypeReferenceQueue_removeHostReference( /** Triggered whenever the sentinel is deleted */ -JNIEXPORT void JNICALL Java_jpype_ref_JPypeReferenceQueue_wake( - JNIEnv *env, jclass clazz, jlong contextPtr) +JNIEXPORT void JNICALL Java_org_jpype_ref_JPypeReferenceNative_wake +(JNIEnv *env, jclass clazz) { // Exceptions are not allowed here try { - JPContext* context = (JPContext*) contextPtr; + JPContext* context = JPContext_global; context->m_GC->triggered(); } catch (...) // GCOVR_EXCL_LINE { } } -JPReferenceQueue::JPReferenceQueue(JPJavaFrame& frame) -{ - JP_TRACE_IN("JPReferenceQueue::init"); - m_Context = frame.getContext(); - - // build the ReferenceQueue class ... - jclass cls = m_Context->getClassLoader() - ->findClass(frame, "org.jpype.ref.JPypeReferenceQueue"); - - //Required due to bug in jvm - //See: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6493522 - frame.GetMethodID(cls, "", "()V"); - - JNINativeMethod method2[2]; - method2[0].name = (char*) "removeHostReference"; - method2[0].signature = (char*) "(JJJ)V"; - method2[0].fnPtr = (void*) &Java_jpype_ref_JPypeReferenceQueue_removeHostReference; - - method2[1].name = (char*) "wake"; - method2[1].signature = (char*) "(J)V"; - method2[1].fnPtr = (void*) &Java_jpype_ref_JPypeReferenceQueue_wake; - - frame.RegisterNatives(cls, method2, 2); - - // Get all required methods - m_ReferenceQueueRegisterMethod = frame.GetMethodID(cls, "registerRef", "(Ljava/lang/Object;JJ)V"); - - JP_TRACE_OUT; // GCOVR_EXCL_LINE -} - -JPReferenceQueue::~JPReferenceQueue() // GCOVR_EXCL_LINE -{ } -void JPReferenceQueue::registerRef(jobject obj, PyObject* hostRef) +void JPReferenceQueue::registerRef(JPJavaFrame &frame, jobject obj, PyObject* hostRef) { // MATCH TO DECREF IN releasePython Py_INCREF(hostRef); - registerRef(obj, hostRef, &releasePython); + registerRef(frame, obj, hostRef, &releasePython); } -void JPReferenceQueue::registerRef(jobject obj, void* host, JCleanupHook func) +void JPReferenceQueue::registerRef(JPJavaFrame &frame, jobject obj, void* host, JCleanupHook func) { JP_TRACE_IN("JPReferenceQueue::registerRef"); - JPJavaFrame frame = JPJavaFrame::outer(m_Context); // create the ref ... jvalue args[3]; @@ -115,7 +97,9 @@ void JPReferenceQueue::registerRef(jobject obj, void* host, JCleanupHook func) args[1].j = (jlong) host; args[2].j = (jlong) func; + if (s_ReferenceQueue == NULL) + JP_RAISE(PyExc_SystemError, "Memory queue not installed"); JP_TRACE("Register reference"); - frame.CallVoidMethodA(m_ReferenceQueue.get(), m_ReferenceQueueRegisterMethod, args); + frame.CallVoidMethodA(s_ReferenceQueue, s_ReferenceQueueRegisterMethod, args); JP_TRACE_OUT; // GCOVR_EXCL_LINE } diff --git a/native/common/jp_shorttype.cpp b/native/common/jp_shorttype.cpp index db808a5d7..b9bee5436 100644 --- a/native/common/jp_shorttype.cpp +++ b/native/common/jp_shorttype.cpp @@ -53,7 +53,7 @@ class JPConversionJShort : public JPConversionJavaValue { public: - virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) + virtual JPMatch::Type matches(JPClass *cls, JPMatch &match) override { JPValue* value = match.getJavaSlot(); if (value == NULL) @@ -86,7 +86,7 @@ class JPConversionJShort : public JPConversionJavaValue return JPMatch::_implicit; //short cut further checks } - void getInfo(JPClass *cls, JPConversionInfo &info) + virtual void getInfo(JPClass *cls, JPConversionInfo &info) override { JPContext *context = cls->getContext(); PyList_Append(info.exact, (PyObject*) context->_short->getHost()); @@ -123,7 +123,7 @@ void JPShortType::getConversionInfo(JPConversionInfo &info) PyList_Append(info.ret, (PyObject*) m_Context->_short->getHost()); } -jarray JPShortType::newArrayInstance(JPJavaFrame& frame, jsize sz) +jarray JPShortType::newArrayOf(JPJavaFrame& frame, jsize sz) { return frame.NewShortArray(sz); } diff --git a/native/common/jp_typefactory.cpp b/native/common/jp_typefactory.cpp index 8663beabb..063477a4a 100644 --- a/native/common/jp_typefactory.cpp +++ b/native/common/jp_typefactory.cpp @@ -85,7 +85,11 @@ template void convert(JPJavaFrame& frame, jlongArray array, vector& #define JP_JAVA_CATCH(...) } catch(...) { JPTypeFactory_rethrow(frame); } return __VA_ARGS__ #endif -JNIEXPORT void JNICALL JPTypeFactory_newWrapper( +extern "C" +{ + + +JNIEXPORT void JNICALL Java_org_jpype_manager_TypeFactoryNative_newWrapper( JNIEnv *env, jobject self, jlong contextPtr, jlong jcls) { JPContext* context = (JPContext*) contextPtr; @@ -97,7 +101,7 @@ JNIEXPORT void JNICALL JPTypeFactory_newWrapper( JP_JAVA_CATCH(); // GCOVR_EXCL_LINE } -JNIEXPORT void JNICALL JPTypeFactory_destroy( +JNIEXPORT void JNICALL Java_org_jpype_manager_TypeFactoryNative_destroy( JNIEnv *env, jobject self, jlong contextPtr, jlongArray resources, jint sz) @@ -116,7 +120,7 @@ JNIEXPORT void JNICALL JPTypeFactory_destroy( JP_JAVA_CATCH(); // GCOVR_EXCL_LINE } -JNIEXPORT jlong JNICALL JPTypeFactory_defineMethodDispatch( +JNIEXPORT jlong JNICALL Java_org_jpype_manager_TypeFactoryNative_defineMethodDispatch( JNIEnv *env, jobject self, jlong contextPtr, jlong clsPtr, jstring name, @@ -136,7 +140,7 @@ JNIEXPORT jlong JNICALL JPTypeFactory_defineMethodDispatch( JP_JAVA_CATCH(0); // GCOVR_EXCL_LINE } -JNIEXPORT jlong JNICALL JPTypeFactory_defineArrayClass( +JNIEXPORT jlong JNICALL Java_org_jpype_manager_TypeFactoryNative_defineArrayClass( JNIEnv *env, jobject self, jlong contextPtr, jclass cls, jstring name, @@ -158,7 +162,7 @@ JNIEXPORT jlong JNICALL JPTypeFactory_defineArrayClass( JP_JAVA_CATCH(0); // GCOVR_EXCL_LINE } -JNIEXPORT jlong JNICALL JPTypeFactory_defineObjectClass( +JNIEXPORT jlong JNICALL Java_org_jpype_manager_TypeFactoryNative_defineObjectClass( JNIEnv *env, jobject self, jlong contextPtr, jclass cls, jstring name, @@ -303,7 +307,7 @@ JNIEXPORT jlong JNICALL JPTypeFactory_defineObjectClass( JP_JAVA_CATCH(0); // GCOVR_EXCL_LINE } -JNIEXPORT jlong JNICALL JPTypeFactory_definePrimitive( +JNIEXPORT jlong JNICALL Java_org_jpype_manager_TypeFactoryNative_definePrimitive( JNIEnv *env, jobject self, jlong contextPtr, jstring name, jclass cls, @@ -365,7 +369,8 @@ JNIEXPORT jlong JNICALL JPTypeFactory_definePrimitive( JP_JAVA_CATCH(0); // GCOVR_EXCL_LINE } -JNIEXPORT void JNICALL JPTypeFactory_assignMembers(JNIEnv *env, jobject self, +JNIEXPORT void JNICALL Java_org_jpype_manager_TypeFactoryNative_assignMembers( + JNIEnv *env, jobject self, jlong contextPtr, jlong clsPtr, jlong ctorMethod, @@ -389,7 +394,7 @@ JNIEXPORT void JNICALL JPTypeFactory_assignMembers(JNIEnv *env, jobject self, JP_JAVA_CATCH(); // GCOVR_EXCL_LINE } -JNIEXPORT jlong JNICALL JPTypeFactory_defineField( +JNIEXPORT jlong JNICALL Java_org_jpype_manager_TypeFactoryNative_defineField( JNIEnv *env, jobject self, jlong contextPtr, jlong cls, jstring name, @@ -414,7 +419,7 @@ JNIEXPORT jlong JNICALL JPTypeFactory_defineField( JP_JAVA_CATCH(0); // GCOVR_EXCL_LINE } -JNIEXPORT jlong JNICALL JPTypeFactory_defineMethod( +JNIEXPORT jlong JNICALL Java_org_jpype_manager_TypeFactoryNative_defineMethod( JNIEnv *env, jobject self, jlong contextPtr, jlong cls, jstring name, jobject method, @@ -438,7 +443,7 @@ JNIEXPORT jlong JNICALL JPTypeFactory_defineMethod( JP_JAVA_CATCH(0); } -JNIEXPORT jlong JNICALL JPTypeFactory_populateMethod( +JNIEXPORT void JNICALL Java_org_jpype_manager_TypeFactoryNative_populateMethod( JNIEnv *env, jobject self, jlong contextPtr, jlong method, jlong returnType, @@ -452,62 +457,8 @@ JNIEXPORT jlong JNICALL JPTypeFactory_populateMethod( convert(frame, argumentTypes, cargs); JPMethod *methodPtr = (JPMethod*) method; methodPtr->setParameters((JPClass*) returnType, cargs); - JP_JAVA_CATCH(0); // GCOVR_EXCL_LINE -} - -JPTypeFactory::~JPTypeFactory() -{ + JP_JAVA_CATCH(); // GCOVR_EXCL_LINE } -JPTypeFactory::JPTypeFactory(JPJavaFrame& frame) -{ - JP_TRACE_IN("JPTypeFactory::init"); - JPContext* context = frame.getContext(); - jclass cls = context->getClassLoader()->findClass(frame, "org.jpype.manager.TypeFactoryNative"); - - JNINativeMethod method[10]; - - method[0].name = (char*) "destroy"; - method[0].signature = (char*) "(J[JI)V"; - method[0].fnPtr = (void*) &JPTypeFactory_destroy; +} // extern "C" - method[1].name = (char*) "defineMethodDispatch"; - method[1].signature = (char*) "(JJLjava/lang/String;[JI)J"; - method[1].fnPtr = (void*) &JPTypeFactory_defineMethodDispatch; - - method[2].name = (char*) "defineArrayClass"; - method[2].signature = (char*) "(JLjava/lang/Class;Ljava/lang/String;JJI)J"; - method[2].fnPtr = (void*) &JPTypeFactory_defineArrayClass; - - method[3].name = (char*) "defineObjectClass"; - method[3].signature = (char*) "(JLjava/lang/Class;Ljava/lang/String;J[JI)J"; - method[3].fnPtr = (void*) &JPTypeFactory_defineObjectClass; - - method[4].name = (char*) "definePrimitive"; - method[4].signature = (char*) "(JLjava/lang/String;Ljava/lang/Class;JI)J"; - method[4].fnPtr = (void*) &JPTypeFactory_definePrimitive; - - method[5].name = (char*) "assignMembers"; - method[5].signature = (char*) "(JJJ[J[J)V"; - method[5].fnPtr = (void*) &JPTypeFactory_assignMembers; - - method[6].name = (char*) "defineField"; - method[6].signature = (char*) "(JJLjava/lang/String;Ljava/lang/reflect/Field;JI)J"; - method[6].fnPtr = (void*) &JPTypeFactory_defineField; - - method[7].name = (char*) "defineMethod"; - method[7].signature = (char*) "(JJLjava/lang/String;Ljava/lang/reflect/Executable;[JI)J"; - method[7].fnPtr = (void*) &JPTypeFactory_defineMethod; - - method[8].name = (char*) "populateMethod"; - method[8].signature = (char*) "(JJJ[J)V"; - method[8].fnPtr = (void*) &JPTypeFactory_populateMethod; - - method[9].name = (char*) "newWrapper"; - method[9].signature = (char*) "(JJ)V"; - method[9].fnPtr = (void*) &JPTypeFactory_newWrapper; - - frame.GetMethodID(cls, "", "()V"); - frame.RegisterNatives(cls, method, 10); - JP_TRACE_OUT; // GCOVR_EXCL_LINE -} diff --git a/native/common/jp_voidtype.cpp b/native/common/jp_voidtype.cpp index d427a8bc5..2ab201acb 100644 --- a/native/common/jp_voidtype.cpp +++ b/native/common/jp_voidtype.cpp @@ -104,7 +104,7 @@ void JPVoidType::setArrayItem(JPJavaFrame& frame, jarray, jsize, PyObject*) JP_RAISE(PyExc_SystemError, "void cannot be the type of an array."); } -jarray JPVoidType::newArrayInstance(JPJavaFrame& frame, jsize) +jarray JPVoidType::newArrayOf(JPJavaFrame& frame, jsize) { JP_RAISE(PyExc_SystemError, "void cannot be the type of an array."); } diff --git a/native/java/org/jpype/JPypeContext.java b/native/java/org/jpype/JPypeContext.java index ddc14c614..a20da5773 100644 --- a/native/java/org/jpype/JPypeContext.java +++ b/native/java/org/jpype/JPypeContext.java @@ -10,7 +10,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + See NOTICE file for details. **************************************************************************** */ package org.jpype; @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import org.jpype.classloader.DynamicClassLoader; import org.jpype.manager.TypeFactory; import org.jpype.manager.TypeFactoryNative; import org.jpype.manager.TypeManager; @@ -70,21 +71,20 @@ public class JPypeContext public final String VERSION = "1.0.2_dev0"; - private static JPypeContext instance = null; + private static JPypeContext INSTANCE = null; // This is the C++ portion of the context. private long context; private TypeFactory typeFactory; private TypeManager typeManager; - private JPypeReferenceQueue referenceQueue; - private ClassLoader bootLoader; - private AtomicInteger shutdownFlag = new AtomicInteger(); - private AtomicInteger proxyCount = new AtomicInteger(); - private List shutdownHooks = new ArrayList<>(); - private List postHooks = new ArrayList<>(); + private DynamicClassLoader classLoader; + private final AtomicInteger shutdownFlag = new AtomicInteger(); + private final AtomicInteger proxyCount = new AtomicInteger(); + private final List shutdownHooks = new ArrayList<>(); + private final List postHooks = new ArrayList<>(); static public JPypeContext getInstance() { - return instance; + return INSTANCE; } /** @@ -94,21 +94,28 @@ static public JPypeContext getInstance() * @param bootLoader is the classloader holding JPype resources. * @return the created context. */ - public static JPypeContext createContext(long context, ClassLoader bootLoader) + static JPypeContext createContext(long context, ClassLoader bootLoader, String nativeLib) { - instance = new JPypeContext(); - - instance.context = context; - instance.bootLoader = bootLoader; - instance.typeFactory = new TypeFactoryNative(); - instance.typeManager = new TypeManager(context, instance.typeFactory); - instance.typeManager.typeFactory = instance.typeFactory; + if (nativeLib != null) + System.load(nativeLib); + INSTANCE = new JPypeContext(context, bootLoader); + INSTANCE.initialize(); + return INSTANCE; + } - instance.referenceQueue = new JPypeReferenceQueue(context); + private JPypeContext(long context, ClassLoader bootLoader) + { + this.context = context; + this.classLoader = (DynamicClassLoader) bootLoader; + this.typeFactory = new TypeFactoryNative(); + this.typeManager = new TypeManager(context, this.typeFactory); + } + void initialize() + { // Okay everything is setup so lets give it a go. - instance.typeManager.init(); - instance.referenceQueue.start(); + this.typeManager.init(); + JPypeReferenceQueue.getInstance().start(); JPypeSignal.installHandlers(); // Install a shutdown hook to clean up Python resources. @@ -117,11 +124,10 @@ public static JPypeContext createContext(long context, ClassLoader bootLoader) @Override public void run() { - instance.shutdown(); + INSTANCE.shutdown(); } })); - return instance; } /** @@ -192,7 +198,6 @@ private void shutdown() { if (t1 == t || t.isDaemon()) continue; -// if (t.getState() == Thread.State.RUNNABLE) t.interrupt(); } @@ -212,17 +217,20 @@ private void shutdown() } } -// // Check to see if who is alive -// threads = Thread.getAllStackTraces(); -// System.out.println("Check for remaining"); -// for (Thread t : threads.keySet()) -// { -// System.out.println(" " + t.getName() + " " + t.getState()); -// for (StackTraceElement e : t.getValue()) +// // Check to see if who is alive +// threads = Thread.getAllStackTraces(); +// System.out.println("Check for remaining"); +// for (Thread t : threads.keySet()) // { -// System.out.println(" " + e.getClassName()); +// // Daemon threads don't count for shutdown so skip them. +// if (t.isDaemon()) +// continue; +// System.out.println(" " + t.getName() + " " + t.getState() + " " + t.isDaemon()); +// for (StackTraceElement e : t.getStackTrace()) +// { +// System.out.println(" " + e.getClassName()); +// } // } -// } } catch (Throwable th) { } @@ -230,7 +238,7 @@ private void shutdown() // Release all Python references try { - this.referenceQueue.stop(); + JPypeReferenceQueue.getInstance().stop(); } catch (Throwable th) { } @@ -248,6 +256,7 @@ private void shutdown() { run.run(); } + } static native void onShutdown(long ctxt); @@ -277,9 +286,9 @@ public long getContext() return context; } - public ClassLoader getBootLoader() + public ClassLoader getClassLoader() { - return this.bootLoader; + return this.classLoader; } public TypeFactory getTypeFactory() @@ -292,11 +301,6 @@ public TypeManager getTypeManager() return this.typeManager; } - public JPypeReferenceQueue getReferenceQueue() - { - return this.referenceQueue; - } - /** * Add a hook to run after Python interface is shutdown. * @@ -457,18 +461,6 @@ public void decrementProxy() proxyCount.decrementAndGet(); } - public static class PyExceptionProxy extends RuntimeException - { - - long cls, value; - - public PyExceptionProxy(long l0, long l1) - { - cls = l0; - value = l1; - } - } - public long getExcClass(Throwable th) { if (th instanceof PyExceptionProxy) diff --git a/native/java/org/jpype/JPypeUtilities.java b/native/java/org/jpype/JPypeUtilities.java new file mode 100644 index 000000000..4aef7471f --- /dev/null +++ b/native/java/org/jpype/JPypeUtilities.java @@ -0,0 +1,21 @@ +package org.jpype; + +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class JPypeUtilities +{ + public static Path getJarPath(Class c) + { + try + { + return Paths.get(c.getProtectionDomain().getCodeSource().getLocation() + .toURI()).getParent(); + } catch (URISyntaxException ex) + { + return null; + } + } + +} diff --git a/native/java/org/jpype/PyExceptionProxy.java b/native/java/org/jpype/PyExceptionProxy.java new file mode 100644 index 000000000..ffbe93184 --- /dev/null +++ b/native/java/org/jpype/PyExceptionProxy.java @@ -0,0 +1,19 @@ +package org.jpype; + +/** + * + * @author nelson85 + */ +public class PyExceptionProxy extends RuntimeException +{ + + long cls; + long value; + + public PyExceptionProxy(long l0, long l1) + { + cls = l0; + value = l1; + } + +} diff --git a/native/java/org/jpype/classloader/DynamicClassLoader.java b/native/java/org/jpype/classloader/DynamicClassLoader.java new file mode 100644 index 000000000..37dd7ead3 --- /dev/null +++ b/native/java/org/jpype/classloader/DynamicClassLoader.java @@ -0,0 +1,139 @@ +package org.jpype.classloader; + +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLConnection; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.LinkedList; +import java.util.List; + +public class DynamicClassLoader extends ClassLoader +{ + + List loaders = new LinkedList<>(); + + public DynamicClassLoader(ClassLoader parent) + { + super(parent); + } + + /** + * Add a set of jars to the classpath. + * + * @param root + * @param glob + * @throws IOException + */ + public void addFiles(Path root, String glob) throws IOException + { + final PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher(glob); + + List urls = new LinkedList<>(); + Files.walkFileTree(root, new SimpleFileVisitor() + { + + @Override + public FileVisitResult visitFile(Path path, + BasicFileAttributes attrs) throws IOException + { + if (pathMatcher.matches(root.relativize(path))) + { + URL url = path.toUri().toURL(); + urls.add(url); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) + throws IOException + { + return FileVisitResult.CONTINUE; + } + }); + + loaders.add(new URLClassLoader(urls.toArray(new URL[urls.size()]))); + } + + public void addFile(Path path) throws FileNotFoundException + { + try + { + if (!Files.exists(path)) + throw new FileNotFoundException(path.toString()); + URL[] urls = new URL[] + { + path.toUri().toURL() + }; + loaders.add(new URLClassLoader(urls)); + } catch (MalformedURLException ex) + { + // This should never happen + throw new RuntimeException(ex); + } + } + + /** + * Loads a class from the class loader. + * + * @param name is the name of the class with java class notation (using dots). + * @return the class + * @throws ClassNotFoundException was not found by the class loader. + * @throws ClassFormatError if the class byte code was invalid. + */ + @Override + public Class findClass(String name) throws ClassNotFoundException, ClassFormatError + { + String aname = name.replace('.', '/') + ".class"; + URL url = this.getResource(aname); + if (url == null) + throw new ClassNotFoundException(name); + + try + { + URLConnection connection = url.openConnection(); + try (InputStream is = connection.getInputStream()) + { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int bytes; + byte[] d = new byte[1024]; + while ((bytes = is.read(d, 0, d.length)) != -1) + { + buffer.write(d, 0, bytes); + } + + buffer.flush(); + byte[] data = buffer.toByteArray(); + return defineClass(name, data, 0, data.length); + } + } catch (IOException ex) + { + } + throw new ClassNotFoundException(name); + } + + @Override + public URL getResource(String name) + { + for (ClassLoader cl : this.loaders) + { + URL url = cl.getResource(name); + if (url == null) + continue; + return url; + } + return null; + } + +} diff --git a/native/java/org/jpype/html/Parser.java b/native/java/org/jpype/html/Parser.java index a1b1397a0..620a28211 100644 --- a/native/java/org/jpype/html/Parser.java +++ b/native/java/org/jpype/html/Parser.java @@ -136,7 +136,6 @@ private void flushTokens(ByteBuffer outgoing) */ protected void processToken(Token token, String value) { -// System.out.println(" TOKEN " + token + " STATE " + state + " " + value); if (token == null) return; Entity entity = add(token, value); @@ -284,20 +283,16 @@ public boolean apply(Parser parser, Entity entity) if (n < pattern.length) return false; ListIterator iter = stack.listIterator(stack.size()); -// System.out.print(" MATCH "); for (int i = 0; i < pattern.length; ++i) { if (!iter.hasPrevious()) return false; Entity next = iter.previous(); -// System.out.print(next.token + " "); if (next.token != pattern[pattern.length - i - 1]) { -// System.out.println(); return false; } } -// System.out.println(" -> y"); execute(parser); return true; } diff --git a/native/java/org/jpype/manager/TypeManager.java b/native/java/org/jpype/manager/TypeManager.java index ca165147c..70158f0a9 100644 --- a/native/java/org/jpype/manager/TypeManager.java +++ b/native/java/org/jpype/manager/TypeManager.java @@ -65,51 +65,59 @@ public TypeManager(long context, TypeFactory typeFactory) // public synchronized void init() { - if (isStarted) - throw new RuntimeException("Cannot be restarted"); - isStarted = true; - isShutdown = false; - try { - this.functionalAnnotation = Class.forName("java.lang.FunctionalInterface") - .asSubclass(Annotation.class); - } catch (ClassNotFoundException ex) - { - // It is okay if we don't find this - } + if (isStarted) + throw new RuntimeException("Cannot be restarted"); + isStarted = true; + isShutdown = false; - // Create the required minimum classes - this.java_lang_Object = createClass(Object.class, true); + try + { + this.functionalAnnotation = Class.forName("java.lang.FunctionalInterface") + .asSubclass(Annotation.class); + } catch (ClassNotFoundException ex) + { + // It is okay if we don't find this + } - // Note that order is very important when creating these initial wrapper - // types. If something inherits from another type then the super class - // will be created without the special flag and the type system won't - // be able to handle the duplicate type properly. - Class[] cls = - { - Class.class, Number.class, CharSequence.class, Throwable.class, - Void.class, Boolean.class, Byte.class, Character.class, - Short.class, Integer.class, Long.class, Float.class, Double.class, - String.class, JPypeProxy.class, - Method.class, Field.class - }; - for (Class c : cls) - { - createClass(c, true); - } + // Create the required minimum classes + this.java_lang_Object = createClass(Object.class, true); - // Create the primitive types - // Link boxed and primitive types so that the wrappers can find them. - createPrimitive("void", Void.TYPE, Void.class); - createPrimitive("boolean", Boolean.TYPE, Boolean.class); - createPrimitive("byte", Byte.TYPE, Byte.class); - createPrimitive("char", Character.TYPE, Character.class); - createPrimitive("short", Short.TYPE, Short.class); - createPrimitive("int", Integer.TYPE, Integer.class); - createPrimitive("long", Long.TYPE, Long.class); - createPrimitive("float", Float.TYPE, Float.class); - createPrimitive("double", Double.TYPE, Double.class); + // Note that order is very important when creating these initial wrapper + // types. If something inherits from another type then the super class + // will be created without the special flag and the type system won't + // be able to handle the duplicate type properly. + Class[] cls = + { + Class.class, Number.class, CharSequence.class, Throwable.class, + Void.class, Boolean.class, Byte.class, Character.class, + Short.class, Integer.class, Long.class, Float.class, Double.class, + String.class, JPypeProxy.class, + Method.class, Field.class + }; + for (Class c : cls) + { + createClass(c, true); + } + + // Create the primitive types + // Link boxed and primitive types so that the wrappers can find them. + createPrimitive("void", Void.TYPE, Void.class); + createPrimitive("boolean", Boolean.TYPE, Boolean.class); + createPrimitive("byte", Byte.TYPE, Byte.class); + createPrimitive("char", Character.TYPE, Character.class); + createPrimitive("short", Short.TYPE, Short.class); + createPrimitive("int", Integer.TYPE, Integer.class); + createPrimitive("long", Long.TYPE, Long.class); + createPrimitive("float", Float.TYPE, Float.class); + createPrimitive("double", Double.TYPE, Double.class); + } catch (Throwable ex) + { + // We can't get debugging information at this point in the process. + ex.printStackTrace(); + throw ex; + } } /** @@ -294,13 +302,14 @@ public synchronized void populateMethod(long wrapper, Executable method) * * @param object is the object to interrogate. * @return the C++ portion or null if the object is null. + * @throws java.lang.InterruptedException */ public long findClassForObject(Object object) throws InterruptedException { Thread th = Thread.currentThread(); if (th.isInterrupted()) { - th.sleep(1); + Thread.sleep(1); } if (object == null) return 0; @@ -327,15 +336,22 @@ public synchronized void shutdown() // Destroy all the resources held in C++ for (ClassDescriptor entry : this.classMap.values()) { - if (entry.constructorDispatch != 0) - destroyer.add(entry.constructorDispatch); + destroyer.add(entry.constructorDispatch); destroyer.add(entry.constructors); destroyer.add(entry.methodDispatch); destroyer.add(entry.methods); destroyer.add(entry.fields); - if (entry.anonymous != 0) - destroyer.add(entry.anonymous); + destroyer.add(entry.anonymous); destroyer.add(entry.classPtr); + + // The same wrapper can appear more than once so blank as we go. + entry.constructorDispatch = 0; + entry.constructors = null; + entry.methodDispatch = null; + entry.methods = null; + entry.fields = null; + entry.anonymous = 0; + entry.classPtr = 0; } destroyer.flush(); @@ -377,24 +393,38 @@ private ClassDescriptor createClass(Class cls, boolean special) if (cls.isArray()) return this.createArrayClass(cls); + return createOrdinaryClass(cls, special, true); + } + + private ClassDescriptor createOrdinaryClass(Class cls, boolean special, boolean bases) + { // Object classes are more work as we need the super information as well. // Make sure all base classes are loaded Class superClass = cls.getSuperclass(); Class[] interfaces = cls.getInterfaces(); ClassDescriptor[] parents = new ClassDescriptor[interfaces.length + 1]; - long[] interfacesPtr = new long[interfaces.length]; + long[] interfacesPtr = null; long superClassPtr = 0; + superClassPtr = 0; if (superClass != null) { parents[0] = this.getClass(superClass); superClassPtr = parents[0].classPtr; } - // Make sure all interfaces are loaded. - for (int i = 0; i < interfaces.length; ++i) + if (bases) { - parents[i + 1] = this.getClass(interfaces[i]); - interfacesPtr[i] = parents[i + 1].classPtr; + interfacesPtr = new long[interfaces.length]; + + // Make sure all interfaces are loaded. + for (int i = 0; i < interfaces.length; ++i) + { + parents[i + 1] = this.getClass(interfaces[i]); + interfacesPtr[i] = parents[i + 1].classPtr; + } + } else + { + interfacesPtr = new long[0]; } // Set up the modifiers @@ -422,7 +452,7 @@ private ClassDescriptor createClass(Class cls, boolean special) interfacesPtr, modifiers); - // + // Cache the wrapper. ClassDescriptor out = new ClassDescriptor(cls, classPtr); this.classMap.put(cls, out); @@ -920,6 +950,8 @@ private class Destroyer void add(long v) { + if (v == 0) + return; queue[index++] = v; if (index == BLOCK_SIZE) flush(); diff --git a/native/java/org/jpype/pkg/JPypePackageManager.java b/native/java/org/jpype/pkg/JPypePackageManager.java index 450b8e3b4..37a889072 100644 --- a/native/java/org/jpype/pkg/JPypePackageManager.java +++ b/native/java/org/jpype/pkg/JPypePackageManager.java @@ -34,6 +34,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import org.jpype.JPypeContext; import org.jpype.JPypeKeywords; /** @@ -53,7 +54,7 @@ public class JPypePackageManager { final static List bases = new ArrayList(); - final static ClassLoader cl = ClassLoader.getSystemClassLoader(); +// final static ClassLoader cl = ClassLoader.getSystemClassLoader(); final static List modules = getModules(); final static FileSystemProvider jfsp = getFileSystemProvider("jar"); final static Map env = new HashMap<>(); @@ -150,6 +151,7 @@ private static FileSystemProvider getFileSystemProvider(String str) { env.put("create", "true"); + ClassLoader cl = ClassLoader.getSystemClassLoader(); URI uri = null; try { @@ -348,6 +350,7 @@ private static void listPackages(List o, Path base, Path p, int depth) */ private static boolean isJarPackage(String name) { + ClassLoader cl = JPypeContext.getInstance().getClassLoader(); try { Enumeration resources = cl.getResources(name); @@ -372,6 +375,7 @@ private static boolean isJarPackage(String name) */ private static void getJarContents(Map out, String packageName) { + ClassLoader cl = JPypeContext.getInstance().getClassLoader(); try { String path = packageName.replace('.', '/'); diff --git a/native/java/org/jpype/proxy/JPypeProxy.java b/native/java/org/jpype/proxy/JPypeProxy.java index 4a7f0d4e3..5c5e36460 100644 --- a/native/java/org/jpype/proxy/JPypeProxy.java +++ b/native/java/org/jpype/proxy/JPypeProxy.java @@ -20,6 +20,7 @@ import java.lang.reflect.Proxy; import org.jpype.JPypeContext; import org.jpype.manager.TypeManager; +import org.jpype.ref.JPypeReferenceQueue; /** * @@ -27,7 +28,7 @@ */ public class JPypeProxy implements InvocationHandler { - + private final static JPypeReferenceQueue referenceQueue = JPypeReferenceQueue.getInstance(); JPypeContext context; public long instance; public long cleanup; @@ -59,7 +60,7 @@ public static JPypeProxy newProxy(JPypeContext context, public Object newInstance() { Object out = Proxy.newProxyInstance(cl, interfaces, this); - context.getReferenceQueue().registerRef(out, instance, cleanup); + referenceQueue.registerRef(out, instance, cleanup); return out; } diff --git a/native/java/org/jpype/ref/JPypeReferenceNative.java b/native/java/org/jpype/ref/JPypeReferenceNative.java new file mode 100644 index 000000000..c7d18f044 --- /dev/null +++ b/native/java/org/jpype/ref/JPypeReferenceNative.java @@ -0,0 +1,33 @@ +package org.jpype.ref; + +import java.lang.reflect.Method; + +/** + * + * @author nelson85 + */ +public class JPypeReferenceNative +{ + + /** + * Native hook to delete a native resource. + * + * @param host is the address of memory in C. + * @param cleanup is the address the function to cleanup the memory. + */ + public static native void removeHostReference(long host, long cleanup); + + /** + * Triggered by the sentinel when a GC starts. + */ + public static native void wake(); + + /** + * Initialize resources. + * + * @param self + * @param m + */ + public static native void init(Object self, Method m); + +} diff --git a/native/java/org/jpype/ref/JPypeReferenceQueue.java b/native/java/org/jpype/ref/JPypeReferenceQueue.java index ed039353f..9b34ed1bd 100644 --- a/native/java/org/jpype/ref/JPypeReferenceQueue.java +++ b/native/java/org/jpype/ref/JPypeReferenceQueue.java @@ -32,24 +32,31 @@ final public class JPypeReferenceQueue extends ReferenceQueue { - public long context = 0; + private final static JPypeReferenceQueue INSTANCE = new JPypeReferenceQueue(); private JPypeReferenceSet hostReferences; private boolean isStopped = false; private Thread queueThread; private Object queueStopMutex = new Object(); - PhantomReference sentinel = null; + private PhantomReference sentinel = null; - // This is required for register natives - public JPypeReferenceQueue() + public static JPypeReferenceQueue getInstance() { + return INSTANCE; } - public JPypeReferenceQueue(long context) + private JPypeReferenceQueue() { super(); - this.context = context; - this.hostReferences = new JPypeReferenceSet(context); + this.hostReferences = new JPypeReferenceSet(); addSentinel(); + JPypeReferenceNative.removeHostReference(0, 0); + try + { + JPypeReferenceNative.init(this, getClass().getDeclaredMethod("registerRef", Object.class, Long.TYPE, Long.TYPE)); + } catch (NoSuchMethodException | SecurityException ex) + { + throw new RuntimeException(ex); + } } /** @@ -69,7 +76,7 @@ public void registerRef(Object javaObject, long host, long cleanup) return; if (isStopped) { - removeHostReference(context, host, cleanup); + JPypeReferenceNative.removeHostReference(host, cleanup); } else { JPypeReference ref = new JPypeReference(this, javaObject, host, cleanup); @@ -138,16 +145,6 @@ public int getQueueSize() } // - /** - * Native hook to delete a native resource. - * - * @param host is the address of memory in C. - * @param cleanup is the address the function to cleanup the memory. - */ - static native void removeHostReference(long context, long host, long cleanup); - - static native void wake(long context); - /** * Thread to monitor the queue and delete resources. */ @@ -167,7 +164,7 @@ public void run() if (ref == sentinel) { addSentinel(); - wake(context); + JPypeReferenceNative.wake(); continue; } if (ref != null) @@ -175,11 +172,10 @@ public void run() long hostRef = ref.hostReference; long cleanup = ref.cleanup; hostReferences.remove(ref); - removeHostReference(context, hostRef, cleanup); + JPypeReferenceNative.removeHostReference(hostRef, cleanup); } } catch (InterruptedException ex) { - // don't know why ... don't really care ... } } diff --git a/native/java/org/jpype/ref/JPypeReferenceSet.java b/native/java/org/jpype/ref/JPypeReferenceSet.java index fb0e6cbe9..3f58f8e5e 100644 --- a/native/java/org/jpype/ref/JPypeReferenceSet.java +++ b/native/java/org/jpype/ref/JPypeReferenceSet.java @@ -27,12 +27,10 @@ public class JPypeReferenceSet static final int SIZE = 256; ArrayList pools = new ArrayList<>(); Pool current; - long context; private int items; - JPypeReferenceSet(long context) + JPypeReferenceSet() { - this.context = context; } int size() @@ -113,7 +111,7 @@ void flush() if (cleanup == 0) continue; ref.cleanup = 0; - JPypeReferenceQueue.removeHostReference(context, hostRef, cleanup); + JPypeReferenceNative.removeHostReference(hostRef, cleanup); } pool.tail = 0; } diff --git a/native/python/include/jp_pythontypes.h b/native/python/include/jp_pythontypes.h index 1fc382ec4..93e91d8e0 100755 --- a/native/python/include/jp_pythontypes.h +++ b/native/python/include/jp_pythontypes.h @@ -344,7 +344,7 @@ class JPPyCallAcquire /* Release the lock. */ ~JPPyCallAcquire(); private: - void* m_State; + long m_State; } ; /** Used when leaving python to an external potentially @@ -359,7 +359,6 @@ class JPPyCallRelease ~JPPyCallRelease(); private: void* m_State1; - void* m_State2; } ; class JPPyBuffer diff --git a/native/python/include/pyjp.h b/native/python/include/pyjp.h index bc8f1aba0..fe2670d2f 100755 --- a/native/python/include/pyjp.h +++ b/native/python/include/pyjp.h @@ -50,6 +50,8 @@ extern "C" { #endif +PyMODINIT_FUNC PyInit__jpype(); + /** * Set the current exception as the cause of a new exception. * @@ -210,5 +212,6 @@ inline JPContext* PyJPModule_getContext() _ASSERT_JVM_RUNNING(context); // GCOVR_EXCL_LINE return context; } +void PyJPModule_loadResources(PyObject* module); #endif /* PYJP_H */ \ No newline at end of file diff --git a/native/python/jp_pythontypes.cpp b/native/python/jp_pythontypes.cpp index 8b81ca9d0..7358eba89 100644 --- a/native/python/jp_pythontypes.cpp +++ b/native/python/jp_pythontypes.cpp @@ -57,14 +57,13 @@ JPPyObject JPPyObject::use(PyObject* obj) * This policy is used when we are given a new reference that we must * destroy. This will steal a reference. * - * claim reference, and decremented when done. Clears errors if NULL. + * claim reference, and decremented when done. */ JPPyObject JPPyObject::accept(PyObject* obj) { JP_TRACE_PY("pyref new(accept)", obj); if (obj == NULL) PyErr_Clear(); - return JPPyObject(obj, 1); } @@ -387,18 +386,15 @@ void JPPyErr::restore(JPPyObject& exceptionClass, JPPyObject& exceptionValue, JP PyErr_Restore(exceptionClass.keepNull(), exceptionValue.keepNull(), exceptionTrace.keepNull()); } +int m_count = 0; JPPyCallAcquire::JPPyCallAcquire() { - PyGILState_STATE* save = new PyGILState_STATE; - *save = PyGILState_Ensure(); - m_State = (void*) save; + m_State = (long) PyGILState_Ensure(); } JPPyCallAcquire::~JPPyCallAcquire() { - PyGILState_STATE* save = (PyGILState_STATE*) m_State; - PyGILState_Release(*save); - delete save; + PyGILState_Release((PyGILState_STATE) m_State); } // This is used when leaving python from to perform some diff --git a/native/python/pyjp_array.cpp b/native/python/pyjp_array.cpp index 1592217c3..3a702e3f8 100644 --- a/native/python/pyjp_array.cpp +++ b/native/python/pyjp_array.cpp @@ -85,7 +85,7 @@ static int PyJPArray_init(PyObject *self, PyObject *args, PyObject *kwargs) jlong length = PySequence_Size(v); if (length < 0 || length > 2147483647) JP_RAISE(PyExc_ValueError, "Array size invalid"); - JPValue newArray = arrayClass->newInstance(frame, (int) length); + JPValue newArray = arrayClass->newArray(frame, (int) length); ((PyJPArray*) self)->m_Array = new JPArray(newArray); ((PyJPArray*) self)->m_Array->setRange(0, (jsize) length, 1, v); PyJPValue_assignJavaSlot(frame, self, newArray); @@ -98,7 +98,7 @@ static int PyJPArray_init(PyObject *self, PyObject *args, PyObject *kwargs) long long length = PyLong_AsLongLong(v); if (length < 0 || length > 2147483647) JP_RAISE(PyExc_ValueError, "Array size invalid"); - JPValue newArray = arrayClass->newInstance(frame, (int) length); + JPValue newArray = arrayClass->newArray(frame, (int) length); ((PyJPArray*) self)->m_Array = new JPArray(newArray); PyJPValue_assignJavaSlot(frame, self, newArray); return 0; diff --git a/native/python/pyjp_class.cpp b/native/python/pyjp_class.cpp index 930c5065b..c40514a7d 100644 --- a/native/python/pyjp_class.cpp +++ b/native/python/pyjp_class.cpp @@ -711,7 +711,7 @@ static PyObject *PyJPClass_array(PyJPClass *self, PyObject *item) long sz = PyLong_AsLong(item); JPClass *cls = self->m_Class->newArrayType(frame, 1); jvalue v; - v.l = (jobject) cls->newArrayInstance(frame, sz); + v.l = (jobject) cls->newArrayOf(frame, sz); return cls->convertToPythonObject(frame, v, true).keep(); } diff --git a/native/python/pyjp_module.cpp b/native/python/pyjp_module.cpp index 2e7eb2db7..c22ddde2c 100644 --- a/native/python/pyjp_module.cpp +++ b/native/python/pyjp_module.cpp @@ -16,10 +16,10 @@ #include "jpype.h" #include "pyjp.h" #include "jp_arrayclass.h" -#include "jp_reference_queue.h" #include "jp_primitive_accessor.h" #include "jp_gc.h" #include "jp_stringtype.h" +#include "jp_classloader.h" void PyJPModule_installGC(PyObject* module); @@ -73,7 +73,7 @@ PyObject* _JMethodCode = NULL; PyObject* _JObjectKey = NULL; PyObject* _JVMNotRunning = NULL; -static void PyJPModule_loadResources(PyObject* module) +void PyJPModule_loadResources(PyObject* module) { // Note that if any resource is missing the user will get // the message: @@ -139,6 +139,7 @@ extern "C" // GCOVR_EXCL_START // This is used exclusively during startup + void Py_SetStringWithCause(PyObject *exception, const char *str) { @@ -347,7 +348,7 @@ static PyObject* PyJPModule_convertToDirectByteBuffer(PyObject* self, PyObject* v.l = frame.NewDirectByteBuffer(vw.view->buf, vw.view->len); // Bind lifespan of the view to the java object. - context->getReferenceQueue()->registerRef(v.l, vw.view, &releaseView); + frame.registerRef(v.l, vw.view, &releaseView); vw.view = 0; JPClass *type = frame.findClassForObject(v.l); return type->convertToPythonObject(frame, v, false).keep(); @@ -536,7 +537,7 @@ PyObject *PyJPModule_gcStats(PyObject* module, PyObject *obj) } // GCOVR_EXCL_STOP -PyObject* PyJPModule_isPackage(PyObject *module, PyObject *pkg) +static PyObject* PyJPModule_isPackage(PyObject *module, PyObject *pkg) { JP_PY_TRY("PyJPModule_isPackage"); if (!PyUnicode_Check(pkg)) @@ -550,6 +551,7 @@ PyObject* PyJPModule_isPackage(PyObject *module, PyObject *pkg) JP_PY_CATCH(NULL); // GCOVR_EXCL_LINE } + // GCOVR_EXCL_START PyObject* examine(PyObject *module, PyObject *other) @@ -811,7 +813,7 @@ static PyObject *PyJPModule_convertBuffer(JPPyBuffer& buffer, PyObject *dtype) // Convert the shape Py_ssize_t subs = 1; Py_ssize_t base = 1; - jintArray jdims = (jintArray) context->_int->newArrayInstance(frame, view.ndim); + jintArray jdims = (jintArray) context->_int->newArrayOf(frame, view.ndim); if (view.shape != NULL) { JPPrimitiveArrayAccessor accessor(frame, jdims, diff --git a/project/jpype_java/nbproject/project.properties b/project/jpype_java/nbproject/project.properties index e69b267c4..76c7151ac 100755 --- a/project/jpype_java/nbproject/project.properties +++ b/project/jpype_java/nbproject/project.properties @@ -50,7 +50,7 @@ debug.test.modulepath=\ dist.archive.excludes= # This directory is removed when the project is cleaned: dist.dir=dist -dist.jar=${dist.dir}/jpype_java.jar +dist.jar=${dist.dir}/org.jpype.jar dist.javadoc.dir=${dist.dir}/javadoc endorsed.classpath= excludes= diff --git a/setup.py b/setup.py index a88c2f7d0..c0d7ef862 100644 --- a/setup.py +++ b/setup.py @@ -19,12 +19,24 @@ # ***************************************************************************** import sys import setupext +from pathlib import Path from setuptools import setup from setuptools import Extension +import glob -jpypeLib = Extension(name='_jpype', **setupext.platform.platform_specific) - -install_requires = ['typing_extensions ; python_version< "3.8"'] +jpypeLib = Extension(name='_jpype', **setupext.platform.Platform( + include_dirs=[Path('native', 'common', 'include'), + Path('native', 'python', 'include'), + Path('native', 'embedded', 'include')], + sources=[Path('native', 'common', '*.cpp'), + Path('native', 'python', '*.cpp'), + Path('native', 'embedded', '*.cpp')], +)) +jpypeJar = Extension(name="org.jpype", + sources=glob.glob(str(Path("native", "java", "**", "*.java")), recursive=True), + language="java", + libraries=["lib/asm-8.0.1.jar"] + ) setup( name='JPype1', @@ -52,22 +64,17 @@ 'Topic :: Software Development', 'Topic :: Scientific/Engineering', ], - packages=[ - 'jpype'], - package_dir={ - 'jpype': 'jpype', - }, - install_requires=install_requires, + packages=['jpype'], + package_dir={'jpype': 'jpype', }, + install_requires=['typing_extensions ; python_version< "3.8"'], tests_require=['pytest'], cmdclass={ - 'build_java': setupext.build_java.BuildJavaCommand, - 'build_thunk': setupext.build_thunk.BuildThunkCommand, 'build_ext': setupext.build_ext.BuildExtCommand, 'test_java': setupext.test_java.TestJavaCommand, 'sdist': setupext.sdist.BuildSourceDistribution, 'test': setupext.pytester.PyTest, }, zip_safe=False, - ext_modules=[jpypeLib], + ext_modules=[jpypeJar, jpypeLib, ], distclass=setupext.dist.Distribution, ) diff --git a/setupext/__init__.py b/setupext/__init__.py index 346b9f279..cdee1ac73 100644 --- a/setupext/__init__.py +++ b/setupext/__init__.py @@ -19,8 +19,6 @@ from . import dist from . import platform from . import build_ext -from . import build_java -from . import build_thunk from . import test_java from . import sdist from . import pytester diff --git a/setupext/build_ext.py b/setupext/build_ext.py index acaa2a05a..93d556973 100644 --- a/setupext/build_ext.py +++ b/setupext/build_ext.py @@ -27,6 +27,7 @@ import glob import re import shlex +import shutil import sysconfig @@ -130,8 +131,8 @@ def write(self): all: $(LIB) rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d)) -build/src/jp_thunk.cpp: $(call rwildcard,native/java,*.java) - python setup.py build_thunk +#build/src/jp_thunk.cpp: $(call rwildcard,native/java,*.java) +# python setup.py build_thunk DEPDIR = build/deps $(DEPDIR): ; @mkdir -p $@ @@ -156,7 +157,7 @@ def write(self): $(LIB): $(OBJS) - $(LINK) $(LINKFLAGS) $(OBJS) -ldl -o $@ + $(LINK) $(OBJS) $(LINKFLAGS) -o $@ -include $(DEPFILES) @@ -184,15 +185,15 @@ class BuildExtCommand(build_ext): 'mingw32': [], } - user_options = build_ext.user_options + \ - [('makefile', None, 'Build a makefile for extensions')] - - def finalize_options(self): - build_ext.finalize_options(self) + user_options = build_ext.user_options + [ + ('makefile', None, 'Build a makefile for extensions'), + ('jar', None, 'Build the jar only'), + ] def initialize_options(self, *args): """omit -Wstrict-prototypes from CFLAGS since its only valid for C code.""" self.makefile = False + self.jar = False import distutils.sysconfig cfg_vars = distutils.sysconfig.get_config_vars() replacement = { @@ -212,7 +213,7 @@ def initialize_options(self, *args): if v.find(r) != -1: v = v.replace(r, t) cfg_vars[k] = v - build_ext.initialize_options(self) + super().initialize_options() def _set_cflags(self): # set compiler flags @@ -229,10 +230,6 @@ def _set_cflags(self): e.extra_link_args.extend(self.lopt[c]) def build_extensions(self): - # We need to create the thunk code - self.run_command("build_java") - self.run_command("build_thunk") - if self.makefile: self.compiler = Makefile(self.compiler) self.force = True @@ -248,17 +245,15 @@ def build_extensions(self): # has to be last call print("Call build extensions") - build_ext.build_extensions(self) + super().build_extensions() def build_extension(self, ext): if ext.language == "java": return self.build_java_ext(ext) + if self.jar: + return print("Call build ext") - return build_ext.build_extension(self, ext) - - def get_outputs(self): - output = build_ext.get_outputs(self) - return output + return super().build_extension(ext) def copy_extensions_to_source(self): build_py = self.get_finalized_command('build_py') @@ -289,7 +284,19 @@ def build_java_ext(self, ext): """Run command.""" java = self.distribution.enable_build_jar - # Try to use the cach if we are not requested build + javac = "javac" + try: + if os.path.exists(os.path.join(os.environ['JAVA_HOME'], 'bin', 'javac')): + javac = '"%s"' % os.path.join(os.environ['JAVA_HOME'], 'bin', 'javac') + except KeyError: + pass + jar = "jar" + try: + if os.path.exists(os.path.join(os.environ['JAVA_HOME'], 'bin', 'jar')): + jar = '"%s"' % os.path.join(os.environ['JAVA_HOME'], 'bin', 'jar') + except KeyError: + pass + # Try to use the cache if we are not requested build if not java: src = os.path.join('native', 'jars') dest = os.path.join('build', 'lib') @@ -298,6 +305,10 @@ def build_java_ext(self, ext): copy_tree(src, dest) return + classpath = "." + if ext.libraries: + classpath = os.path.pathsep.join(ext.libraries) + distutils.log.info( "Jar cache is missing, using --enable-build-jar to recreate it.") @@ -307,12 +318,12 @@ def build_java_ext(self, ext): # build the jar try: dirname = os.path.dirname(self.get_ext_fullpath("JAVA")) - jar = os.path.join(dirname, ext.name + ".jar") + jarFile = os.path.join(dirname, ext.name + ".jar") build_dir = os.path.join(self.build_temp, ext.name, "classes") os.makedirs(build_dir, exist_ok=True) os.makedirs(dirname, exist_ok=True) - cmd1 = shlex.split('javac -d %s -g:none -source %s -target %s' % - (build_dir, target_version, target_version)) + cmd1 = shlex.split('%s -cp "%s" -d "%s" -g:none -source %s -target %s' % + (javac, classpath, build_dir, target_version, target_version)) cmd1.extend(ext.sources) debug = "-g:none" if coverage: @@ -320,8 +331,18 @@ def build_java_ext(self, ext): os.makedirs("build/classes", exist_ok=True) self.announce(" %s" % " ".join(cmd1), level=distutils.log.INFO) subprocess.check_call(cmd1) + try: + for file in glob.iglob("native/java/**/*.*", recursive=True): + if file.endswith(".java") or os.path.isdir(file): + continue + p = os.path.join(build_dir, os.path.relpath(file, "native/java")) + print("Copy file", file, p) + shutil.copyfile(file, p) + except Exception as ex: + print("FAIL", ex) + pass cmd3 = shlex.split( - 'jar cvf %s -C %s .' % (jar, build_dir)) + '%s cvf "%s" -C "%s" .' % (jar, jarFile, build_dir)) self.announce(" %s" % " ".join(cmd3), level=distutils.log.INFO) subprocess.check_call(cmd3) diff --git a/setupext/build_java.py b/setupext/build_java.py deleted file mode 100644 index 724f3ef10..000000000 --- a/setupext/build_java.py +++ /dev/null @@ -1,116 +0,0 @@ -# -*- coding: utf-8 -*- -# ***************************************************************************** -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# See NOTICE file for details. -# -# ***************************************************************************** -import os -import sys -import subprocess -import distutils.cmd -import distutils.log -from distutils.errors import DistutilsPlatformError -from distutils.dir_util import copy_tree -import glob -import re -import shlex -import shutil - - -def compileJava(self, coverage): - javac = "javac" - try: - if os.path.exists(os.path.join(os.environ['JAVA_HOME'], 'bin', 'javac')): - javac = '"%s"' % os.path.join(os.environ['JAVA_HOME'], 'bin', 'javac') - except KeyError: - pass - jar = "jar" - try: - if os.path.exists(os.path.join(os.environ['JAVA_HOME'], 'bin', 'jar')): - jar = '"%s"' % os.path.join(os.environ['JAVA_HOME'], 'bin', 'jar') - except KeyError: - pass - target_version = "1.8" - srcs = glob.glob('native/java/**/*.java', recursive=True) - src1 = [i for i in srcs if "JPypeClassLoader" in i] - src2 = [i for i in srcs if not "JPypeClassLoader" in i] - cmd1 = shlex.split('%s -d build/lib -g:lines -source %s -target %s' % - (javac, target_version, target_version)) - cmd1.extend(src1) - debug = "-g:lines" - if coverage: - debug = "-g:lines,vars,source" - cmd2 = shlex.split('%s -d build/classes %s -source %s -target %s -cp build/lib' % - (javac, debug, target_version, target_version)) - cmd2.extend(src2) - os.makedirs("build/lib", exist_ok=True) - try: - shutil.copytree('native/java/', 'build/classes', ignore=shutil.ignore_patterns('*.java')) - except Exception: - pass - self.announce(" %s" % " ".join(cmd1), level=distutils.log.INFO) - subprocess.check_call(cmd1) - self.announce(" %s" % " ".join(cmd2), level=distutils.log.INFO) - subprocess.check_call(cmd2) - jar = "jar" - try: - if os.path.exists(os.path.join(os.environ['JAVA_HOME'], 'bin', 'jar')): - jar = '"%s"' % os.path.join(os.environ['JAVA_HOME'], 'bin', 'jar') - except KeyError: - pass - cmd3 = shlex.split( - '"%s" cvf build/lib/org.jpype.jar -C build/classes/ .' % jar) - self.announce(" %s" % " ".join(cmd3), level=distutils.log.INFO) - subprocess.check_call(cmd3) - - -class BuildJavaCommand(distutils.cmd.Command): - """A custom command to create jar file during build.""" - - description = 'build jpype jar' - user_options = [] - - def initialize_options(self): - """Set default values for options.""" - pass - - def finalize_options(self): - """Post-process options.""" - pass - - def run(self): - """Run command.""" - java = self.distribution.enable_build_jar - - # Try to use the cach if we are not requested build - if not java: - src = os.path.join('native', 'jars') - dest = os.path.join('build', 'lib') - if os.path.exists(src): - distutils.log.info("Using Jar cache") - copy_tree(src, dest) - return - - distutils.log.info( - "Jar cache is missing, using --enable-build-jar to recreate it.") - - coverage = self.distribution.enable_coverage - - # build the jar - try: - compileJava(self, coverage) - except subprocess.CalledProcessError as exc: - distutils.log.error(exc.output) - raise DistutilsPlatformError("Error executing {}".format(exc.cmd)) diff --git a/setupext/build_thunk.py b/setupext/build_thunk.py deleted file mode 100644 index 4b9def2f4..000000000 --- a/setupext/build_thunk.py +++ /dev/null @@ -1,170 +0,0 @@ -# -*- coding: utf-8 -*- -# ***************************************************************************** -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# See NOTICE file for details. -# -# ***************************************************************************** -from __future__ import print_function -import fnmatch -import os -import distutils.cmd -import distutils.log -import array -import sys - -if (sys.version_info < (3, 0)): - import string - - def translate(s, cfrom, cto): - return s.translate(string.maketrans(cfrom, cto)) -else: - def translate(s, cfrom, cto): - return s.translate(str.maketrans(cfrom, cto)) - - -# Python2/3 don't agree on how glob should work - - -def _glob(directory, pattern): - out = [] - for root, dirnames, filenames in os.walk(directory): - for filename in fnmatch.filter(filenames, pattern): - out.append(os.path.join(root, filename)) - return out - - -def output(fout, l): - print(" ", file=fout, end="") - line = [] - buffer = array.array("B") - buffer.frombytes(l) - for i in buffer: - line.append("(jbyte)0x%02X" % i) - print(",".join(line), file=fout, end="") - - -def outputClass(srcfname, cname, fout): - f = open(srcfname, "rb") - chunk = 16 - print("jbyte %s[] = {" % cname, file=fout) - sz = 0 - while True: - l = f.read(chunk) - if len(l) == 0: - break - if sz > 0: - print(",", file=fout) - output(fout, l) - sz += len(l) - - print(file=fout) - print("};", file=fout) - print("int %s_size = %d;" % (cname, sz), file=fout) - print(file=fout) - f.close() - - -def mkFileDir(filename): - if not os.path.exists(os.path.dirname(filename)): - try: - os.makedirs(os.path.dirname(filename)) - except OSError as exc: # Guard against race condition - if exc.errno != errno.EEXIST: - raise - - -def createThunks(input_dir, output_src, output_header, namespace="Thunk"): - mkFileDir(output_src) - mkFileDir(output_header) - - # Write the header - with open(output_header, "w+") as fheader: - sz = len(input_dir) - guard = translate(output_header.upper(), '/\\.', '___') - print("#ifndef %s" % guard, file=fheader) - print("#define %s" % guard, file=fheader) - print("#include ", file=fheader) - print("namespace %s {" % namespace, file=fheader) - for filename in _glob(input_dir, "*.class"): - name = translate(filename, '/\\.', '___')[sz:-6] - print("extern jbyte %s[];" % name, file=fheader) - print("extern int %s_size;" % name, file=fheader) - for filename in _glob(input_dir, "*.jar"): - name = translate(filename, '/\\.', '___')[sz:-4] - print("extern jbyte %s[];" % name, file=fheader) - print("extern int %s_size;" % name, file=fheader) - print("}", file=fheader) - print("#endif", file=fheader) - - # Write the body - with open(output_src, "w+") as fimpl: - sz = len(input_dir) - print("#include ", file=fimpl) - print("namespace %s {" % namespace, file=fimpl) - for filename in _glob(input_dir, "*.class"): - print(" including thunk %s" % filename) - name = translate(filename, '/\\.', '___')[sz:-6] - outputClass(filename, name, fimpl) - for filename in _glob(input_dir, "*.jar"): - print(" including thunk %s" % filename) - name = translate(filename, '/\\.', '___')[sz:-4] - outputClass(filename, name, fimpl) - print("}", file=fimpl) - - -class BuildThunkCommand(distutils.cmd.Command): - """A custom command to create thunk file.""" - - description = 'build dynamic code thunks' - user_options = [ - ] - - def initialize_options(self): - """Set default values for options.""" - pass - - def finalize_options(self): - """Post-process options.""" - pass - - def run(self): - """Run command.""" - self.run_command("build_java") - self.announce( - 'Building thunks', - level=distutils.log.INFO) - # run short circuit logic here - srcDir = os.path.join("build", "lib") - destBody = os.path.join("build", "src", "jp_thunk.cpp") - destHeader = os.path.join("build", "src", "jp_thunk.h") - - if os.path.isfile(destBody): - t1 = os.path.getctime(destBody) - update = False - for filename in _glob(srcDir, "*.class"): - if t1 < os.path.getctime(filename): - update = True - if not update: - self.announce( - 'Skip build thunks', - level=distutils.log.INFO) - return - - # do the build - createThunks( - srcDir, - destBody, - destHeader, - namespace="JPThunk") diff --git a/setupext/platform.py b/setupext/platform.py index 18cc7e42b..f0220d132 100644 --- a/setupext/platform.py +++ b/setupext/platform.py @@ -20,79 +20,82 @@ import setupext import os import sys +import sysconfig # This handles all of the work to make our platform specific extension options. -platform_specific = { - 'include_dirs': [ - os.path.join('native', 'common', 'include'), - os.path.join('native', 'python', 'include'), - os.path.join('build', 'src'), - ], - 'sources': [ - os.path.join('build', 'src', 'jp_thunk.cpp') - ] + setupext.utils.find_sources(), -} - -fallback_jni = os.path.join('native', 'jni_include') -# try to include JNI first from eventually given JAVA_HOME, then from distributed -java_home = os.getenv('JAVA_HOME', '') -found_jni = False -if os.path.exists(java_home) and sys.platform != "cygwin": - platform_specific['include_dirs'] += [os.path.join(java_home, 'include')] - - # check if jni.h can be found - for d in platform_specific['include_dirs']: - if os.path.exists(os.path.join(d, 'jni.h')): - print("Found native jni.h at %s" % d) - found_jni = True - break + +def Platform(include_dirs=[], sources=[]): + platform_specific = { + 'include_dirs': include_dirs, + 'sources': setupext.utils.find_sources(sources), + } + + fallback_jni = os.path.join('native', 'jni_include') + # try to include JNI first from eventually given JAVA_HOME, then from distributed + java_home = os.getenv('JAVA_HOME', '') + found_jni = False + if os.path.exists(java_home): + platform_specific['include_dirs'] += [os.path.join(java_home, 'include')] + + # check if jni.h can be found + for d in platform_specific['include_dirs']: + if os.path.exists(os.path.join(str(d), 'jni.h')): + print("Found native jni.h at %s" % d) + found_jni = True + break + + if not found_jni: + warnings.warn('Falling back to provided JNI headers, since your provided' + ' JAVA_HOME "%s" does not provide jni.h' % java_home) if not found_jni: - warnings.warn('Falling back to provided JNI headers, since your provided' - ' JAVA_HOME "%s" does not provide jni.h' % java_home) - -if not found_jni: - platform_specific['include_dirs'] += [fallback_jni] - -if sys.platform == 'win32': - platform_specific['libraries'] = ['Advapi32'] - platform_specific['define_macros'] = [('WIN32', 1)] - if sys.version > '3': - platform_specific['extra_compile_args'] = [ - '/Zi', '/EHsc', '/std:c++14'] + platform_specific['include_dirs'] += [fallback_jni] + + platform_specific['extra_link_args'] = [] + if sys.platform == 'win32': + platform_specific['libraries'] = ['Advapi32'] + platform_specific['define_macros'] = [('WIN32', 1)] + if sys.version > '3': + platform_specific['extra_compile_args'] = [ + '/Zi', '/EHsc', '/std:c++14'] + else: + platform_specific['extra_compile_args'] = ['/Zi', '/EHsc'] + platform_specific['extra_link_args'] = ['/DEBUG'] + jni_md_platform = 'win32' + + elif sys.platform == 'darwin': + platform_specific['libraries'] = ['dl'] + platform_specific['define_macros'] = [('MACOSX', 1)] + platform_specific['extra_compile_args'] = ['-g0', '-std=c++11'] + jni_md_platform = 'darwin' + + elif sys.platform.startswith('linux'): + platform_specific['libraries'] = ['dl'] + platform_specific['extra_compile_args'] = ['-g0', '-std=c++11'] + jni_md_platform = 'linux' + + elif sys.platform.startswith('aix7'): + platform_specific['libraries'] = ['dl'] + platform_specific['extra_compile_args'] = ['-g3', '-std=c++11'] + jni_md_platform = 'aix7' + + elif sys.platform.startswith('freebsd'): + jni_md_platform = 'freebsd' + else: - platform_specific['extra_compile_args'] = ['/Zi', '/EHsc'] - platform_specific['extra_link_args'] = ['/DEBUG'] - jni_md_platform = 'win32' - -elif sys.platform == 'darwin': - platform_specific['libraries'] = ['dl'] - platform_specific['define_macros'] = [('MACOSX', 1)] - platform_specific['extra_compile_args'] = ['-g0', '-std=c++11'] - jni_md_platform = 'darwin' - -elif sys.platform.startswith('linux'): - platform_specific['libraries'] = ['dl'] - platform_specific['extra_compile_args'] = ['-g0', '-std=c++11'] - jni_md_platform = 'linux' - -elif sys.platform.startswith('aix7'): - platform_specific['libraries'] = ['dl'] - platform_specific['extra_compile_args'] = ['-g3', '-std=c++11'] - jni_md_platform = 'aix7' - -elif sys.platform.startswith('freebsd'): - jni_md_platform = 'freebsd' - -else: - jni_md_platform = None - warnings.warn("Your platform %s is not being handled explicitly." - " It may work or not!" % sys.platform, UserWarning) - -if found_jni: - platform_specific['include_dirs'] += \ - [os.path.join(java_home, 'include', jni_md_platform)] + jni_md_platform = None + warnings.warn("Your platform %s is not being handled explicitly." + " It may work or not!" % sys.platform, UserWarning) + + if sysconfig.get_config_var('BLDLIBRARY') is not None: + platform_specific['extra_link_args'].append(sysconfig.get_config_var('BLDLIBRARY')) + + if found_jni: + platform_specific['include_dirs'] += \ + [os.path.join(java_home, 'include', jni_md_platform)] + return platform_specific + # include this stolen from FindJNI.cmake """ diff --git a/setupext/sdist.py b/setupext/sdist.py index e7aaea99a..9e2779c5c 100644 --- a/setupext/sdist.py +++ b/setupext/sdist.py @@ -18,7 +18,8 @@ # ***************************************************************************** import os import distutils -from distutils.dir_util import copy_tree, remove_tree +from distutils.dir_util import copy_tree, remove_tree, mkpath +from distutils.file_util import copy_file from setuptools.command.sdist import sdist # Customization of the sdist @@ -32,16 +33,27 @@ class BuildSourceDistribution(sdist): """ def run(self): + dest = os.path.join('native', 'jars') + # We need to build a jar cache for the source distribution - self.run_command("build_java") + cmd = self.distribution.get_command_obj('build_ext') + + # Call with jar only option + cmd.jar = True + self.run_command('build_ext') + + # Find out the location of the jar file + dirname = os.path.dirname(cmd.get_ext_fullpath("JAVA")) + jarFile = os.path.join(dirname, "org.jpype.jar") + + # Also build the test harness files self.run_command("test_java") - dest = os.path.join('native', 'jars') - src = os.path.join('build', 'lib') - if not os.path.exists(src): + if not os.path.exists(jarFile): distutils.log.error("Jar source file is missing from build") raise distutils.errors.DistutilsPlatformError( "Error copying jar file") - copy_tree(src, dest) + mkpath(dest) + copy_file(jarFile, dest) # Collect the sources sdist.run(self) diff --git a/setupext/utils.py b/setupext/utils.py index d8876ebfd..48eda99db 100644 --- a/setupext/utils.py +++ b/setupext/utils.py @@ -18,6 +18,7 @@ # ***************************************************************************** import os import codecs +import glob def read_utf8(path, *parts): @@ -25,10 +26,9 @@ def read_utf8(path, *parts): return codecs.open(filename, encoding='utf-8').read() -def find_sources(): +def find_sources(roots=[]): cpp_files = [] - for dirpath, dirnames, filenames in os.walk(os.path.join('native')): - for filename in filenames: - if filename.endswith('.cpp') or filename.endswith('.c'): - cpp_files.append(os.path.join(dirpath, filename)) + for root in roots: + for filename in glob.iglob(str(root)): + cpp_files.append(filename) return cpp_files diff --git a/test/jpypetest/test_javacoverage.py b/test/jpypetest/test_javacoverage.py index c58798cb3..477830b0b 100644 --- a/test/jpypetest/test_javacoverage.py +++ b/test/jpypetest/test_javacoverage.py @@ -42,13 +42,6 @@ def testContext(self): JArray(JObject)([None, None])), None) self.assertEqual(self.inst.getExcValue(None), 0) - def testRefQueue(self): - refqueue = self.inst.getReferenceQueue() - self.assertIsInstance(refqueue.getQueueSize(), int) - self.assertTrue(refqueue.isRunning()) - cls = JClass('org.jpype.ref.JPypeReferenceQueue') - cls() - def testReference(self): JPypeReference = JClass('org.jpype.ref.JPypeReference') u = JPypeReference(None, None, 0, 0) @@ -62,9 +55,8 @@ def testModifiers(self): self.assertEqual(cls.get(cls.decode(1171)), 1171) def testTypeFactory(self): - bl = self.inst.getBootLoader() - TypeFactory = JClass("org.jpype.manager.TypeFactory", bl, True) - TypeManager = JClass("org.jpype.manager.TypeManager", bl, True) + TypeFactory = JClass("org.jpype.manager.TypeFactory") + TypeManager = JClass("org.jpype.manager.TypeManager") @JImplements(TypeFactory) class TF(object): def __init__(self): diff --git a/test/jpypetest/test_ref.py b/test/jpypetest/test_ref.py index f30913311..1aa7dfe83 100644 --- a/test/jpypetest/test_ref.py +++ b/test/jpypetest/test_ref.py @@ -26,10 +26,8 @@ class ReferenceQueueTestCase(common.JPypeTestCase): def setUp(self): common.JPypeTestCase.setUp(self) - self.classLoader = jpype.JClass( - 'org.jpype.JPypeContext').getInstance().getBootLoader() self.refqueue = jpype.JClass( - 'org.jpype.JPypeContext').getInstance().getReferenceQueue() + 'org.jpype.ref.JPypeReferenceQueue').getInstance() def testAccess(self): # Make sure we can get the instance