mirror of https://github.com/kivy/pyjnius.git
various fix for java/python implementation. It finally start working, but still lot to do.
Use: make testimplem, and check the jnius/jnius_utils.pxi:test() function.
This commit is contained in:
parent
9b758a81c3
commit
dcfe369f1e
4
Makefile
4
Makefile
|
@ -14,3 +14,7 @@ tests: build_ext
|
|||
cd tests && javac org/jnius/InterfaceWithPublicEnum.java
|
||||
cd tests && javac org/jnius/ClassArgument.java
|
||||
cd tests && env PYTHONPATH=..:$(PYTHONPATH) nosetests -v
|
||||
|
||||
testimpl: build_ext
|
||||
javac jnius/NativeInvocationHandler.java
|
||||
python -c 'import jnius.jnius; jnius.jnius.test()'
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package jnius;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class NativeInvocationHandler implements InvocationHandler {
|
||||
public NativeInvocationHandler(long ptr) {
|
||||
this.ptr = ptr;
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) {
|
||||
/**
|
||||
if ( method.getName() == "toString" ) {
|
||||
System.out.println("+ java:invoke toString.");
|
||||
return "<proxy>";
|
||||
}
|
||||
/**/
|
||||
System.out.print("+ java:invoke(<proxy>, ");
|
||||
// don't call it, or recursive lookup/proxy will go!
|
||||
//System.out.print(proxy);
|
||||
//System.out.print(", ");
|
||||
System.out.print(method);
|
||||
System.out.print(", ");
|
||||
System.out.print(args);
|
||||
System.out.println(")");
|
||||
System.out.println(method.getName());
|
||||
System.out.println(method.getParameterTypes());
|
||||
Object ret = invoke0(proxy, method, args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
native Object invoke0(Object proxy, Method method, Object[] args);
|
||||
|
||||
private long ptr;
|
||||
}
|
|
@ -41,6 +41,8 @@ cdef extern from "jni.h":
|
|||
const_char* signature
|
||||
void* fnPtr
|
||||
|
||||
ctypedef JNINativeMethod const_JNINativeMethod "const JNINativeMethod"
|
||||
|
||||
ctypedef union jvalue:
|
||||
jboolean z
|
||||
jbyte b
|
||||
|
@ -368,7 +370,7 @@ cdef extern from "jni.h":
|
|||
jsize, jsize, const_jdouble*)
|
||||
|
||||
#XXX not working with cython?
|
||||
#jint (*RegisterNatives)(JNIEnv*, jclass, const_JNINativeMethod*, jint)
|
||||
jint (*RegisterNatives)(JNIEnv*, jclass, const_JNINativeMethod*, jint)
|
||||
jint (*UnregisterNatives)(JNIEnv*, jclass)
|
||||
jint (*MonitorEnter)(JNIEnv*, jobject)
|
||||
jint (*MonitorExit)(JNIEnv*, jobject)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
cdef void release_args(JNIEnv *j_env, list definition_args, jvalue *j_args, args) except *:
|
||||
cdef void release_args(JNIEnv *j_env, tuple definition_args, jvalue *j_args, args) except *:
|
||||
# do the conversion from a Python object to Java from a Java definition
|
||||
cdef JavaObject jo
|
||||
cdef JavaClass jc
|
||||
|
@ -15,11 +15,12 @@ cdef void release_args(JNIEnv *j_env, list definition_args, jvalue *j_args, args
|
|||
j_env[0].DeleteLocalRef(j_env, j_args[index].l)
|
||||
|
||||
|
||||
cdef void populate_args(JNIEnv *j_env, list definition_args, jvalue *j_args, args) except *:
|
||||
cdef void populate_args(JNIEnv *j_env, tuple definition_args, jvalue *j_args, args) except *:
|
||||
# do the conversion from a Python object to Java from a Java definition
|
||||
cdef JavaClassStorage jcs
|
||||
cdef JavaObject jo
|
||||
cdef JavaClass jc
|
||||
cdef PythonJavaClass pc
|
||||
cdef int index
|
||||
for index, argtype in enumerate(definition_args):
|
||||
py_arg = args[index]
|
||||
|
@ -59,6 +60,13 @@ cdef void populate_args(JNIEnv *j_env, list definition_args, jvalue *j_args, arg
|
|||
elif isinstance(py_arg, MetaJavaClass):
|
||||
jcs = py_arg.__cls_storage
|
||||
j_args[index].l = jcs.j_cls
|
||||
elif isinstance(py_arg, PythonJavaClass):
|
||||
# from python class, get the proxy/python class
|
||||
pc = py_arg
|
||||
# get the java class
|
||||
jc = pc.j_self
|
||||
# get the localref
|
||||
j_args[index].l = jc.j_self.obj
|
||||
elif isinstance(py_arg, (tuple, list)):
|
||||
j_args[index].l = convert_pyarray_to_java(j_env, argtype, py_arg)
|
||||
else:
|
||||
|
|
|
@ -63,7 +63,7 @@ class MetaJavaClass(type):
|
|||
interfaces[n] = jcs.j_env[0].FindClass(jcs.j_env, <char*>i)
|
||||
|
||||
getProxyClass = jcs.j_env[0].GetStaticMethodID(
|
||||
jcs.j_env, Proxy, "getProxyClass",
|
||||
jcs.j_env, baseclass, "getProxyClass",
|
||||
"(Ljava/lang/ClassLoader,[Ljava/lang/Class;)Ljava/lang/Class;")
|
||||
|
||||
getClassLoader = jcs.j_env[0].GetStaticMethodID(
|
||||
|
@ -76,7 +76,7 @@ class MetaJavaClass(type):
|
|||
jargs[0] = classLoader
|
||||
jargs[1] = interfaces
|
||||
jcs.j_cls = jcs.j_env[0].CallStaticObjectMethod(
|
||||
jcs.j_env, Proxy, getProxyClass, jargs)
|
||||
jcs.j_env, baseclass, getProxyClass, jargs)
|
||||
|
||||
if jcs.j_cls == NULL:
|
||||
raise JavaException('Unable to create the class'
|
||||
|
@ -93,6 +93,7 @@ class MetaJavaClass(type):
|
|||
# search all the static JavaMethod within our class, and resolve them
|
||||
cdef JavaMethod jm
|
||||
cdef JavaMultipleMethod jmm
|
||||
cdef PythonMethod pm
|
||||
for name, value in classDict.iteritems():
|
||||
if isinstance(value, JavaMethod):
|
||||
jm = value
|
||||
|
@ -105,13 +106,13 @@ class MetaJavaClass(type):
|
|||
jmm.set_resolve_info(jcs.j_env, jcs.j_cls, None,
|
||||
name, __javaclass__)
|
||||
elif isinstance(value, PythonMethod):
|
||||
if '__javabaseclass__' not in self.classDict:
|
||||
if '__javabaseclass__' not in classDict:
|
||||
raise JavaException("Can't use PythonMethod on a java "
|
||||
"class, you must use inheritance to implement a java "
|
||||
"interface")
|
||||
pm = value
|
||||
pm.set_resolve_info(self.j_env, self.j_cls, self.j_self,
|
||||
name, self.__javaclass__)
|
||||
pm.set_resolve_info(jcs.j_env, jcs.j_cls, jcs.j_self,
|
||||
name, jcs.__javaclass__)
|
||||
|
||||
|
||||
# search all the static JavaField within our class, and resolve them
|
||||
|
@ -235,6 +236,7 @@ cdef class JavaClass(object):
|
|||
# search all the JavaMethod within our class, and resolve them
|
||||
cdef JavaMethod jm
|
||||
cdef JavaMultipleMethod jmm
|
||||
cdef PythonMethod pm
|
||||
for name, value in self.__class__.__dict__.iteritems():
|
||||
if isinstance(value, JavaMethod):
|
||||
jm = value
|
||||
|
@ -247,7 +249,6 @@ cdef class JavaClass(object):
|
|||
jmm.set_resolve_info(self.j_env, self.j_cls, self.j_self,
|
||||
name, self.__javaclass__)
|
||||
elif isinstance(value, PythonMethod):
|
||||
# if self#XXX
|
||||
pm = value
|
||||
pm.set_resolve_info(self.j_env, self.j_cls, self.j_self,
|
||||
name, self.__javaclass__)
|
||||
|
@ -473,6 +474,18 @@ cdef class PythonMethod(object):
|
|||
cdef bytes classname
|
||||
# XXX
|
||||
|
||||
cdef void set_resolve_info(self, JNIEnv *j_env, jclass j_cls, LocalRef j_self,
|
||||
bytes name, bytes classname):
|
||||
'''
|
||||
XXX TODO
|
||||
self.name = name
|
||||
self.classname = classname
|
||||
self.j_env = j_env
|
||||
self.j_cls = j_cls
|
||||
self.j_self = j_self
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
cdef class JavaMethod(object):
|
||||
'''Used to resolve a Java method, and do the call
|
||||
|
@ -539,7 +552,7 @@ cdef class JavaMethod(object):
|
|||
def __call__(self, *args):
|
||||
# argument array to pass to the method
|
||||
cdef jvalue *j_args = NULL
|
||||
cdef list d_args = self.definition_args
|
||||
cdef tuple d_args = self.definition_args
|
||||
if self.is_varargs:
|
||||
args = args[:len(d_args) - 1] + (args[len(d_args) - 1:],)
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ cdef parse_definition(definition):
|
|||
c, argdef = argdef.split(';', 1)
|
||||
args.append(prefix + c + ';')
|
||||
|
||||
return ret, args
|
||||
return ret, tuple(args)
|
||||
|
||||
|
||||
cdef void check_exception(JNIEnv *j_env) except *:
|
||||
|
@ -211,6 +211,10 @@ cdef int calculate_score(sign_args, args, is_varargs=False) except *:
|
|||
score += 1
|
||||
continue
|
||||
|
||||
if isinstance(arg, PythonJavaClass):
|
||||
score += 1
|
||||
continue
|
||||
|
||||
# native type? not accepted
|
||||
return -1
|
||||
|
||||
|
@ -237,7 +241,7 @@ cdef int calculate_score(sign_args, args, is_varargs=False) except *:
|
|||
# change this method score
|
||||
return score
|
||||
|
||||
|
||||
'''
|
||||
cdef class GenericNativeWrapper(object):
|
||||
"""
|
||||
This class is to be used to register python method as methods of
|
||||
|
@ -615,6 +619,20 @@ cdef class GenericNativeWrapper(object):
|
|||
# va_end(j_args)
|
||||
|
||||
# return self.callback(*args)
|
||||
'''
|
||||
|
||||
import functools
|
||||
class java_implementation(object):
|
||||
def __init__(self, signature):
|
||||
super(java_implementation, self).__init__()
|
||||
self.signature = signature
|
||||
|
||||
def __get__(self, instance, instancetype):
|
||||
return functools.partial(self.__call__, instance)
|
||||
|
||||
def __call__(self, f):
|
||||
f.__javasignature__ = self.signature
|
||||
return f
|
||||
|
||||
cdef class PythonJavaClass(object):
|
||||
'''
|
||||
|
@ -622,42 +640,158 @@ cdef class PythonJavaClass(object):
|
|||
'''
|
||||
cdef JNIEnv *j_env
|
||||
cdef jclass j_cls
|
||||
cdef LocalRef j_self
|
||||
cdef public object j_self
|
||||
|
||||
def __cinit__(self, *args):
|
||||
self.j_env = NULL
|
||||
self.j_env = get_jnienv()
|
||||
self.j_cls = NULL
|
||||
self.j_self = NULL
|
||||
pass
|
||||
self.j_self = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.j_self = create_proxy_instance()
|
||||
self.j_self = create_proxy_instance(self.j_env, self,
|
||||
self.__javainterfaces__)
|
||||
|
||||
cdef jobject invoke0(
|
||||
JNIEnv *j_env,
|
||||
jobject this,
|
||||
jobject method,
|
||||
jobjectArray args):
|
||||
# discover all the java method implementated
|
||||
self.__javamethods__ = {}
|
||||
for x in dir(self):
|
||||
attr = getattr(self, x)
|
||||
if not callable(attr):
|
||||
continue
|
||||
if not hasattr(attr, '__javasignature__'):
|
||||
continue
|
||||
signature = parse_definition(attr.__javasignature__)
|
||||
self.__javamethods__[(x, signature)] = attr
|
||||
|
||||
cdef jfieldID ptrField = j_env[0].GetFieldID(j_env.GetObjectClass(this), "ptr", "J")
|
||||
cdef jlong jptr = j_env.GetLongField(this, ptrField)
|
||||
return h.fnPtr(env, method, args);
|
||||
def invoke(self, method, *args):
|
||||
from .reflect import get_signature
|
||||
print 'PythonJavaClass.invoke() called with args:', args
|
||||
# search the java method
|
||||
|
||||
ret_signature = get_signature(method.getReturnType())
|
||||
args_signature = tuple([get_signature(x) for x in method.getParameterTypes()])
|
||||
method_name = method.getName()
|
||||
|
||||
key = (method_name, (ret_signature, args_signature))
|
||||
print 'PythonJavaClass.invoke() want to invoke', key
|
||||
|
||||
py_method = self.__javamethods__.get(key, None)
|
||||
if not py_method:
|
||||
raise NotImplemented('The method {0} is not implemented'.format(key))
|
||||
|
||||
return py_method(*args)
|
||||
|
||||
cdef jobject invoke0(JNIEnv *j_env, jobject j_this, jobject j_proxy, jobject j_method, jobjectArray args):
|
||||
from .reflect import get_signature
|
||||
|
||||
# get the python object
|
||||
cdef jfieldID ptrField = j_env[0].GetFieldID(j_env,
|
||||
j_env[0].GetObjectClass(j_env, j_this), "ptr", "J")
|
||||
cdef jlong jptr = j_env[0].GetLongField(j_env, j_this, ptrField)
|
||||
cdef object py_obj = <object>jptr
|
||||
|
||||
# extract the method information
|
||||
# TODO: cache ?
|
||||
method = convert_jobject_to_python(j_env, b'Ljava/lang/reflect/Method;', j_method)
|
||||
ret_signature = get_signature(method.getReturnType())
|
||||
args_signature = ''.join([get_signature(x) for x in method.getParameterTypes()])
|
||||
|
||||
# XX implement java array conversion
|
||||
py_args = []
|
||||
ret = py_obj.invoke(method, *py_args)
|
||||
|
||||
# convert back to the return type
|
||||
# use the populate_args(), but in the reverse way :)
|
||||
cdef jvalue j_ret[1]
|
||||
populate_args(j_env, (ret_signature, ), <jvalue *>j_ret, [ret])
|
||||
return <jobject>j_ret
|
||||
|
||||
|
||||
# now we need to create a proxy and pass it an invocation handler
|
||||
|
||||
from jnius import autoclass
|
||||
Proxy = autoclass('java.lang.reflec.Proxy')
|
||||
NativeInvocationHandler('jnius.NativeInvocationHandler')
|
||||
cdef create_proxy_instance(JNIEnv *j_env, py_obj, j_interfaces):
|
||||
from .reflect import autoclass
|
||||
Proxy = autoclass('java.lang.reflect.Proxy')
|
||||
NativeInvocationHandler = autoclass('jnius.NativeInvocationHandler')
|
||||
ClassLoader = autoclass('java.lang.ClassLoader')
|
||||
|
||||
def create_proxy_instance(j_env, py_obj, j_interfaces):
|
||||
nih = NativeInvocationHandler(py_obj)
|
||||
cls = Proxy.newProxyInstance(Null, j_interfaces, nih) # XXX wishful code
|
||||
# convert strings to Class
|
||||
j_interfaces = [find_javaclass(x) for x in j_interfaces]
|
||||
print 'create_proxy_instance', j_interfaces
|
||||
|
||||
for name, definition, method in py_obj.j_methods:
|
||||
nw = GenericNativeWrapper(j_env, name, definition, method)
|
||||
j_env.RegisterNatives(j_env[0], cls, nw.nm, 1)
|
||||
cdef JavaClass nih = NativeInvocationHandler(<long><void *>py_obj)
|
||||
cdef JNINativeMethod invoke_methods[1]
|
||||
invoke_methods[0].name = 'invoke0'
|
||||
invoke_methods[0].signature = '(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;'
|
||||
invoke_methods[0].fnPtr = <void *>&invoke0
|
||||
j_env[0].RegisterNatives(j_env, nih.j_cls, <JNINativeMethod *>invoke_methods, 1)
|
||||
|
||||
cdef JavaClass j_obj = Proxy.newProxyInstance(
|
||||
ClassLoader.getSystemClassLoader(), j_interfaces, nih)
|
||||
|
||||
#for name, definition, method in py_obj.j_methods:
|
||||
# nw = GenericNativeWrapper(j_env, name, definition, method)
|
||||
# j_env.RegisterNatives(j_env[0], cls, nw.nm, 1)
|
||||
|
||||
# adds it to the invocationhandler
|
||||
|
||||
# create the proxy and pass it the invocation handler
|
||||
return j_obj
|
||||
|
||||
def test():
|
||||
from .reflect import autoclass
|
||||
|
||||
print '1: declare a TestImplem that implement Collection'
|
||||
class TestImplemIterator(PythonJavaClass):
|
||||
__javainterfaces__ = ['java/util/Iterator']
|
||||
|
||||
def __init__(self, collection):
|
||||
super(TestImplemIterator, self).__init__()
|
||||
self.collection = collection
|
||||
self.index = 0
|
||||
|
||||
@java_implementation('()B')
|
||||
def hasNext(self):
|
||||
return self.index < len(self.collection.data)
|
||||
|
||||
@java_implementation('()Ljava/lang/Object;')
|
||||
def next(self):
|
||||
obj = self.collection.data[self.index]
|
||||
self.index += 1
|
||||
return obj
|
||||
|
||||
|
||||
class TestImplem(PythonJavaClass):
|
||||
__javainterfaces__ = ['java/util/Collection']
|
||||
|
||||
def __init__(self, *args):
|
||||
super(TestImplem, self).__init__()
|
||||
self.data = args
|
||||
|
||||
@java_implementation('()Ljava/util/Iterator;')
|
||||
def iterator(self):
|
||||
it = TestImplemIterator(self)
|
||||
print 'iterator called, and returned', it
|
||||
return it
|
||||
|
||||
print '2: instanciate the class, with some data'
|
||||
a = TestImplem(129387, 'aoesrch', 987, 'aoenth')
|
||||
print a
|
||||
print dir(a)
|
||||
|
||||
print '3: Do cast to a collection'
|
||||
a2 = cast('java/util/Collection', a.j_self)
|
||||
|
||||
print '4: Try few method on the collection'
|
||||
Collections = autoclass('java.util.Collections')
|
||||
print Collections.enumeration(a)
|
||||
#print Collections.enumeration(a)
|
||||
print Collections.max(a)
|
||||
|
||||
|
||||
# XXX We have issues for methosd with multiple signature
|
||||
#print '-> Collections.max(a)'
|
||||
#print Collections.max(a2)
|
||||
#print '-> Collections.max(a)'
|
||||
#print Collections.max(a2)
|
||||
#print '-> Collections.shuffle(a)'
|
||||
#print Collections.shuffle(a2)
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
package jnius;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class NativeInvocationHandler implements InvocationHandler {
|
||||
public NativeInvocationHandler(long ptr) {
|
||||
this.ptr = ptr;
|
||||
}
|
||||
|
||||
Object invoke(Object proxy, Method method, Object[] args) {
|
||||
return invoke0(proxy, method, args);
|
||||
}
|
||||
|
||||
native private Object invoke0(Object proxy, Method method, Object[] args);
|
||||
|
||||
private long ptr;
|
||||
}
|
Loading…
Reference in New Issue