diff --git a/.gitignore b/.gitignore index 459dd9b..d10f9f5 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ local.properties .project .classpath .settings +bin/ \ No newline at end of file diff --git a/build.gradle b/build.gradle index e3952ef..d81cebe 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,7 @@ for (target in ['ios', 'macos', 'windows', 'ndk']) { allprojects { group 'org.moe' - version '1.1.3' + version '1.1.5' } /**************************** diff --git a/natj-win/mingw.mutex.h b/natj-win/mingw.mutex.h index 191ed98..095b7fe 100755 --- a/natj-win/mingw.mutex.h +++ b/natj-win/mingw.mutex.h @@ -191,7 +191,7 @@ class timed_mutex: public _NonRecursiveMutex public: using base::base; template - void try_lock_for(const std::chrono::duration& dur) + bool try_lock_for(const std::chrono::duration& dur) { bool ret = base::try_lock_for(dur); #ifdef STDTHREAD_STRICT_NONRECURSIVE_LOCKS diff --git a/src/main/java/org/moe/natj/general/NatJ.java b/src/main/java/org/moe/natj/general/NatJ.java index 071f4d7..50ae699 100644 --- a/src/main/java/org/moe/natj/general/NatJ.java +++ b/src/main/java/org/moe/natj/general/NatJ.java @@ -30,9 +30,12 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; @@ -919,6 +922,71 @@ public static class NativeObjectConstructionInfo { public Mapper mapper; } + public static Annotation[][] getParameterAnnotationsInherited(Method method) { + Class[] parameterTypes = method.getParameterTypes(); + if (parameterTypes.length == 0) { + return new Annotation[0][]; + } + + List> results = new ArrayList<>(parameterTypes.length); + for (int i = 0; i < parameterTypes.length; i++) { + results.add(new ArrayList()); + } + + ArrayList> supers = new ArrayList<>(); + // First get annotations from interfaces + Class methodDeclaredKlass = method.getDeclaringClass(); + Class[] interfaces = methodDeclaredKlass.getInterfaces(); + supers.addAll(Arrays.asList(interfaces)); + // Then super class + Class superClass = methodDeclaredKlass.getSuperclass(); + if (superClass != null) { + supers.add(superClass); + } + + for (Class c : supers) { + // Find method + try { + for (Method sm : c.getDeclaredMethods()) { + int modifiers = sm.getModifiers(); + if (Modifier.isStatic(modifiers) + || Modifier.isPrivate(modifiers) + || Modifier.isFinal(modifiers)) { + continue; + } + + if (method.getName().equals(sm.getName()) && Arrays.equals(parameterTypes, sm.getParameterTypes())) { + int idx = 0; + for (Annotation[] annotations : sm.getParameterAnnotations()) { + results.get(idx).addAll(Arrays.asList(annotations)); + idx++; + } + break; + } + } + } catch (Throwable e) { + // Do nothing + } + } + + // Finally add annotations from current method + int idx = 0; + for (Annotation[] annotations : method.getParameterAnnotations()) { + results.get(idx).addAll(Arrays.asList(annotations)); + idx++; + } + + // Convert list to array + Annotation[][] resultArray = new Annotation[parameterTypes.length][]; + for (int i = 0; i < parameterTypes.length; i++) { + List al = results.get(i); + Annotation[] aa = new Annotation[al.size()]; + aa = al.toArray(aa); + resultArray[i] = aa; + } + return resultArray; + } + /** * Constructs a {@link JavaObjectConstructionInfo} instance. * diff --git a/src/main/java/org/moe/natj/objc/map/ObjCCallbackMapper.java b/src/main/java/org/moe/natj/objc/map/ObjCCallbackMapper.java index 537c7d4..32f54f7 100644 --- a/src/main/java/org/moe/natj/objc/map/ObjCCallbackMapper.java +++ b/src/main/java/org/moe/natj/objc/map/ObjCCallbackMapper.java @@ -20,7 +20,6 @@ import org.moe.natj.general.NatJ; import org.moe.natj.general.Pointer; import org.moe.natj.general.ann.Runtime; -import org.moe.natj.objc.ObjCObject; import org.moe.natj.objc.ObjCRuntime; import org.moe.natj.objc.WeakReference; import org.moe.natj.objc.ann.ObjCBlock; @@ -126,30 +125,79 @@ public static Pointer createStrongBlockBindingPointer(long peer, boolean owned) /** * Base for invocation handlers. + * + * Handling of hashCode(), equals() and toString() use the sample code from + * https://docs.oracle.com/javase/8/docs/technotes/guides/reflection/proxy.html */ private abstract static class BlockInvocationHandler implements InvocationHandler { + // preloaded Method objects for the methods in java.lang.Object + private static Method hashCodeMethod; + private static Method equalsMethod; + private static Method toStringMethod; + static { + try { + hashCodeMethod = Object.class.getMethod("hashCode", null); + equalsMethod = Object.class.getMethod("equals", new Class[] { Object.class }); + toStringMethod = Object.class.getMethod("toString", null); + } catch (NoSuchMethodException e) { + throw new NoSuchMethodError(e.getMessage()); + } + } + + private final String name; public Pointer peer; public long data; - protected BlockInvocationHandler(Pointer peer, long data) { + protected BlockInvocationHandler(String name, Pointer peer, long data) { + this.name = name; this.peer = peer; this.data = data; } @Override - public abstract Object invoke(Object proxy, Method method, Object[] args); + public Object invoke(Object proxy, Method method, Object[] args) { + Class declaringClass = method.getDeclaringClass(); + + if (declaringClass == Object.class) { + if (method.equals(hashCodeMethod)) { + return proxyHashCode(proxy); + } else if (method.equals(equalsMethod)) { + return proxyEquals(proxy, args[0]); + } else if (method.equals(toStringMethod)) { + return proxyToString(proxy); + } else { + throw new InternalError("unexpected Object method dispatched: " + method); + } + } else { + return invoke0(proxy, method, args); + } + } + + protected Integer proxyHashCode(Object proxy) { + return new Integer(System.identityHashCode(proxy)); + } + + protected Boolean proxyEquals(Object proxy, Object other) { + return (proxy == other ? Boolean.TRUE : Boolean.FALSE); + } + + protected String proxyToString(Object proxy) { + return name + '@' + Integer.toHexString(proxy.hashCode()); + } + + protected abstract Object invoke0(Object proxy, Method method, Object[] args); } /** * Invocation handler for native blocks with boolean return value. */ private static class BooleanInvocationHandler extends BlockInvocationHandler { - public BooleanInvocationHandler(Pointer peer, long data) { - super(peer, data); + public BooleanInvocationHandler(String name, Pointer peer, long data) { + super(name, peer, data); } @Override - public Object invoke(Object proxy, Method method, Object[] args) { + public Object invoke0(Object proxy, Method method, Object[] args) { return new Boolean(ObjCRuntime.forwardBooleanBlockCall(peer.getPeer(), data, args)); } } @@ -158,12 +206,12 @@ public Object invoke(Object proxy, Method method, Object[] args) { * Invocation handler for native blocks with byte return value. */ private static class ByteInvocationHandler extends BlockInvocationHandler { - public ByteInvocationHandler(Pointer peer, long data) { - super(peer, data); + public ByteInvocationHandler(String name, Pointer peer, long data) { + super(name, peer, data); } @Override - public Object invoke(Object proxy, Method method, Object[] args) { + public Object invoke0(Object proxy, Method method, Object[] args) { return new Byte(ObjCRuntime.forwardByteBlockCall(peer.getPeer(), data, args)); } } @@ -172,12 +220,12 @@ public Object invoke(Object proxy, Method method, Object[] args) { * Invocation handler for native blocks with char return value. */ private static class CharacterInvocationHandler extends BlockInvocationHandler { - public CharacterInvocationHandler(Pointer peer, long data) { - super(peer, data); + public CharacterInvocationHandler(String name, Pointer peer, long data) { + super(name, peer, data); } @Override - public Object invoke(Object proxy, Method method, Object[] args) { + public Object invoke0(Object proxy, Method method, Object[] args) { return new Character(ObjCRuntime.forwardCharBlockCall(peer.getPeer(), data, args)); } } @@ -186,12 +234,12 @@ public Object invoke(Object proxy, Method method, Object[] args) { * Invocation handler for native blocks with short return value. */ private static class ShortInvocationHandler extends BlockInvocationHandler { - public ShortInvocationHandler(Pointer peer, long data) { - super(peer, data); + public ShortInvocationHandler(String name, Pointer peer, long data) { + super(name, peer, data); } @Override - public Object invoke(Object proxy, Method method, Object[] args) { + public Object invoke0(Object proxy, Method method, Object[] args) { return new Short(ObjCRuntime.forwardShortBlockCall(peer.getPeer(), data, args)); } } @@ -200,12 +248,12 @@ public Object invoke(Object proxy, Method method, Object[] args) { * Invocation handler for native blocks with int return value. */ private static class IntegerInvocationHandler extends BlockInvocationHandler { - public IntegerInvocationHandler(Pointer peer, long data) { - super(peer, data); + public IntegerInvocationHandler(String name, Pointer peer, long data) { + super(name, peer, data); } @Override - public Object invoke(Object proxy, Method method, Object[] args) { + public Object invoke0(Object proxy, Method method, Object[] args) { return new Integer(ObjCRuntime.forwardIntBlockCall(peer.getPeer(), data, args)); } } @@ -214,12 +262,12 @@ public Object invoke(Object proxy, Method method, Object[] args) { * Invocation handler for native blocks with long return value. */ private static class LongInvocationHandler extends BlockInvocationHandler { - public LongInvocationHandler(Pointer peer, long data) { - super(peer, data); + public LongInvocationHandler(String name, Pointer peer, long data) { + super(name, peer, data); } @Override - public Object invoke(Object proxy, Method method, Object[] args) { + public Object invoke0(Object proxy, Method method, Object[] args) { return new Long(ObjCRuntime.forwardLongBlockCall(peer.getPeer(), data, args)); } } @@ -228,12 +276,12 @@ public Object invoke(Object proxy, Method method, Object[] args) { * Invocation handler for native blocks with float return value. */ private static class FloatInvocationHandler extends BlockInvocationHandler { - public FloatInvocationHandler(Pointer peer, long data) { - super(peer, data); + public FloatInvocationHandler(String name, Pointer peer, long data) { + super(name, peer, data); } @Override - public Object invoke(Object proxy, Method method, Object[] args) { + public Object invoke0(Object proxy, Method method, Object[] args) { return new Float(ObjCRuntime.forwardFloatBlockCall(peer.getPeer(), data, args)); } } @@ -242,12 +290,12 @@ public Object invoke(Object proxy, Method method, Object[] args) { * Invocation handler for native blocks with double return value. */ private static class DoubleInvocationHandler extends BlockInvocationHandler { - public DoubleInvocationHandler(Pointer peer, long data) { - super(peer, data); + public DoubleInvocationHandler(String name, Pointer peer, long data) { + super(name, peer, data); } @Override - public Object invoke(Object proxy, Method method, Object[] args) { + public Object invoke0(Object proxy, Method method, Object[] args) { return new Double(ObjCRuntime.forwardDoubleBlockCall(peer.getPeer(), data, args)); } } @@ -256,12 +304,12 @@ public Object invoke(Object proxy, Method method, Object[] args) { * Invocation handler for native blocks with boolean return value. */ private static class VoidInvocationHandler extends BlockInvocationHandler { - public VoidInvocationHandler(Pointer peer, long data) { - super(peer, data); + public VoidInvocationHandler(String name, Pointer peer, long data) { + super(name, peer, data); } @Override - public Object invoke(Object proxy, Method method, Object[] args) { + public Object invoke0(Object proxy, Method method, Object[] args) { ObjCRuntime.forwardVoidBlockCall(peer.getPeer(), data, args); return null; } @@ -271,12 +319,12 @@ public Object invoke(Object proxy, Method method, Object[] args) { * Invocation handler for native blocks with boolean return value. */ private static class ObjectInvocationHandler extends BlockInvocationHandler { - public ObjectInvocationHandler(Pointer peer, long data) { - super(peer, data); + public ObjectInvocationHandler(String name, Pointer peer, long data) { + super(name, peer, data); } @Override - public Object invoke(Object proxy, Method method, Object[] args) { + public Object invoke0(Object proxy, Method method, Object[] args) { return ObjCRuntime.forwardObjectBlockCall(peer.getPeer(), data, args); } } @@ -571,7 +619,7 @@ private Object objectToJava(long peer, NatJ.JavaObjectConstructionInfo info) { } try { blockInfo.handlerConstructor = handler.getConstructor( - Pointer.class, Long.TYPE); + String.class, Pointer.class, Long.TYPE); blockInfo.proxyConstructor = Proxy.getProxyClass( info.type.getClassLoader(), new Class[] { info.type @@ -592,7 +640,7 @@ private Object objectToJava(long peer, NatJ.JavaObjectConstructionInfo info) { } try { instance = blockInfo.proxyConstructor.newInstance(blockInfo.handlerConstructor - .newInstance(pointer, blockInfo.data)); + .newInstance(info.type.getName(), pointer, blockInfo.data)); } catch (Exception e) { throw new RuntimeException("Java object construction error!", e); } diff --git a/src/main/native/natj/NatJ.cpp b/src/main/native/natj/NatJ.cpp index 3d78603..62f43b4 100644 --- a/src/main/native/natj/NatJ.cpp +++ b/src/main/native/natj/NatJ.cpp @@ -133,6 +133,7 @@ jmethodID gBuildNativeObjectInfoStaticMethod = NULL; jmethodID gGetMappedMethod = NULL; jmethodID gGetMappedReturnMethod = NULL; jmethodID gGetParameterAnnotationsMethod = NULL; +jmethodID gGetParameterAnnotationsInheritedStaticMethod = NULL; jmethodID gGetRuntimeStaticMethod = NULL; jmethodID gGetNativeExceptionMethod = NULL; jmethodID gGetMessageMethod = NULL; @@ -403,6 +404,9 @@ void JNICALL Java_org_moe_natj_general_NatJ_initialize(JNIEnv* env, jclass clazz gGetParameterAnnotationsMethod = env->GetMethodID(gMethodClass, "getParameterAnnotations", "()[[Ljava/lang/annotation/Annotation;"); + gGetParameterAnnotationsInheritedStaticMethod = + env->GetStaticMethodID(gNatJClass, "getParameterAnnotationsInherited", + "(Ljava/lang/reflect/Method;)[[Ljava/lang/annotation/Annotation;"); gGetRuntimeStaticMethod = env->GetStaticMethodID( gNatJClass, "getRuntime", "(Ljava/lang/Class;Z)Lorg/moe/natj/general/NativeRuntime;"); @@ -866,8 +870,8 @@ void buildInfos(JNIEnv* env, jobject method, bool toJava, jobject** paramInfos, #endif jobjectArray parameterTypes = (jobjectArray)env->CallObjectMethod(method, gGetParameterTypesMethod); - jobjectArray parameterAnns = (jobjectArray)env->CallObjectMethod( - method, gGetParameterAnnotationsMethod); + jobjectArray parameterAnns = (jobjectArray)env->CallStaticObjectMethod( + gNatJClass, gGetParameterAnnotationsInheritedStaticMethod, method); jsize parameterCount = env->GetArrayLength(parameterTypes); if (isVariadic) { parameterCount--; diff --git a/src/main/native/natj/NatJ.h b/src/main/native/natj/NatJ.h index 89a73c3..0adb508 100644 --- a/src/main/native/natj/NatJ.h +++ b/src/main/native/natj/NatJ.h @@ -507,6 +507,7 @@ extern jmethodID gBuildNativeObjectInfoStaticMethod; extern jmethodID gGetMappedMethod; extern jmethodID gGetMappedReturnMethod; extern jmethodID gGetParameterAnnotationsMethod; +extern jmethodID gGetParameterAnnotationsInheritedStaticMethod; extern jmethodID gGetRuntimeStaticMethod; extern jmethodID gGetNativeExceptionMethod; extern jmethodID gGetMessageMethod; diff --git a/src/main/native/natj/ObjCRuntime.mm b/src/main/native/natj/ObjCRuntime.mm index 17b53ee..e73d86b 100644 --- a/src/main/native/natj/ObjCRuntime.mm +++ b/src/main/native/natj/ObjCRuntime.mm @@ -85,6 +85,7 @@ static Class gBlockClass = NSClassFromString(@"NSBlock"); static Class gStackBlockClass = NSClassFromString(@"__NSStackBlock"); +static Class gStackBlockClass2 = NSClassFromString(@"__NSStackBlock__"); static int8_t gDefaultUnboxPolicy; @@ -985,7 +986,8 @@ jboolean Java_org_moe_natj_objc_ObjCRuntime_isObjectBlock(JNIEnv* env, jboolean Java_org_moe_natj_objc_ObjCRuntime_isStackBlock(JNIEnv* env, jclass clazz, jlong object) { - return [reinterpret_cast(object) isKindOfClass:gStackBlockClass]; + return [reinterpret_cast(object) isKindOfClass:gStackBlockClass] + || (gStackBlockClass2 != nil && [reinterpret_cast(object) isKindOfClass:gStackBlockClass2]); } jlong Java_org_moe_natj_objc_ObjCRuntime_copyBlock(JNIEnv* env,