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:
Mathieu Virbel 2012-12-31 05:15:09 +01:00
parent 9b758a81c3
commit dcfe369f1e
7 changed files with 229 additions and 51 deletions

View File

@ -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()'

View File

@ -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;
}

View File

@ -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)

View File

@ -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:

View File

@ -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:],)

View File

@ -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)

View File

@ -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;
}