diff --git a/.gitignore b/.gitignore index c3592143..1d706680 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ dalvik .classpath .project .settings +.DS_Store diff --git a/.travis.yml b/.travis.yml index 8b1c552b..d669105a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ jdk: - oraclejdk8 #- oraclejdk7 #- openjdk7 - - openjdk6 + #- openjdk6 compiler: - gcc diff --git a/pom.xml b/pom.xml index ec9a1b41..0cbadc49 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ bridj BridJ (NativeLibs4Java C/C++ Interop Layer) http://code.google.com/p/bridj/ - 0.7.1-SNAPSHOT + 0.8.0-SNAPSHOT bundle @@ -25,6 +25,11 @@ v0_7_0 true + 1.8 + 1.8 + 1.8 + 1.8 + 1.6 scm:git:git@github.com:nativelibs4java/BridJ.git @@ -48,6 +53,11 @@ asm 5.0.3 + + com.fasterxml + classmate + 1.3.3 + org.osgi org.osgi.core @@ -109,6 +119,53 @@ + + maven-compiler-plugin + 3.6.0 + + ${maven.compiler.source} + ${maven.compiler.target} + ${maven.compiler.testSource} + ${maven.compiler.testTarget} + + + + default-compile + compile + + compile + + + ${maven.compiler.source} + ${maven.compiler.target} + + + + default-testCompile + test-compile + + testCompile + + + ${maven.compiler.testSource} + ${maven.compiler.testTarget} + + + + + + net.orfjackal.retrolambda + retrolambda-maven-plugin + 2.4.0 + + + + process-main + process-test + + + + org.apache.felix maven-bundle-plugin @@ -314,6 +371,7 @@ jar + ${maven.compiler.source} Core Packages @@ -526,5 +584,14 @@ BridJ is opensource software. Please refer to LICENSE.BridJ.txt to know under wh + + disable-java8-doclint + + [1.8,) + + + -Xdoclint:none + + diff --git a/src/main/java/org/bridj/StructFieldDeclaration.java b/src/main/java/org/bridj/StructFieldDeclaration.java index 2be78874..bff1821d 100644 --- a/src/main/java/org/bridj/StructFieldDeclaration.java +++ b/src/main/java/org/bridj/StructFieldDeclaration.java @@ -30,12 +30,7 @@ */ package org.bridj; -import static org.bridj.util.AnnotationUtils.isAnnotationPresent; - -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Member; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; @@ -44,11 +39,18 @@ import org.bridj.ann.Bits; import org.bridj.ann.Field; import org.bridj.ann.Union; +import org.bridj.util.Methods; +import org.bridj.util.Types; + +import com.fasterxml.classmate.ResolvedTypeWithMembers; +import com.fasterxml.classmate.members.ResolvedField; +import com.fasterxml.classmate.members.ResolvedMember; +import com.fasterxml.classmate.members.ResolvedMethod; class StructFieldDeclaration { final StructFieldDescription desc = new StructFieldDescription(); - Method setter; + ResolvedMethod setter; long index = -1, unionWith = -1;//, byteOffset = -1; Class valueClass; Class declaringClass; @@ -57,79 +59,87 @@ class StructFieldDeclaration { public String toString() { return desc.name + " (index = " + index + (unionWith < 0 ? "" : ", unionWith = " + unionWith) + ", desc = " + desc + ")"; } - - protected static boolean acceptFieldGetter(Member member, boolean getter) { - if ((member instanceof Method) && ((Method) member).getParameterTypes().length != (getter ? 0 : 1)) { - return false; - } - - if (((AnnotatedElement) member).getAnnotation(Field.class) == null) { - return false; - } - - int modifiers = member.getModifiers(); - - return !Modifier.isStatic(modifiers); - } - - /** - * Creates a list of structure fields - */ + + protected static boolean acceptFieldGetter(ResolvedMember member, boolean getter) { + if ((member instanceof ResolvedMethod) && ((ResolvedMethod) member).getRawMember().getParameterTypes().length != (getter ? 0 : 1)) { + return false; + } + + if (member.get(Field.class) == null) { + return false; + } + + return !member.isStatic(); + } + protected static List listFields(Class structClass) { - List list = new ArrayList(); - for (Method method : structClass.getMethods()) { - if (acceptFieldGetter(method, true)) { - StructFieldDeclaration io = fromGetter(method); - try { - Method setter = structClass.getMethod(method.getName(), io.valueClass); - if (acceptFieldGetter(setter, false)) { - io.setter = setter; - } - } catch (Exception ex) { - //assert BridJ.info("No setter for getter " + method); - } - if (io != null) { - list.add(io); + List list = new ArrayList(); + ResolvedTypeWithMembers resolvedStruct = Types.resolveTypeWithInstanceMethods(structClass); + for (ResolvedMethod method : resolvedStruct.getMemberMethods()) { + if (acceptFieldGetter(method, true)) { + StructFieldDeclaration io = fromGetter(method); + try { + // this only works when the names are equal, does not support setXXX methods. + ResolvedMethod setter = Methods.getMethod( resolvedStruct.getMemberMethods(), method.getName(), io.valueClass); + if (acceptFieldGetter(setter, false)) { + io.setter = setter; } + } catch (Exception ex) { + //assert BridJ.info("No setter for getter " + method); + } + if (io != null) { + list.add(io); } } + } - int nFieldFields = 0; - for (java.lang.reflect.Field field : structClass.getFields()) { - if (acceptFieldGetter(field, true)) { - StructFieldDeclaration io = StructFieldDeclaration.fromField(field); - if (io != null) { - list.add(io); - nFieldFields++; - } + int nFieldFields = 0; + for ( ResolvedField field : resolvedStruct.getMemberFields()) { + if (acceptFieldGetter(field, true)) { + StructFieldDeclaration io = StructFieldDeclaration.fromField(field); + if (io != null) { + list.add(io); + nFieldFields++; } } - if (nFieldFields > 0 && BridJ.warnStructFields) { - BridJ.warning("Struct " + structClass.getName() + " has " + nFieldFields + " struct fields implemented as Java fields, which won't give the best performance and might require counter-intuitive calls to BridJ.readFromNative / .writeToNative. Please consider using JNAerator to generate your struct instead, or use BRIDJ_WARN_STRUCT_FIELDS=0 or -Dbridj.warnStructFields=false to mute this warning."); - } - - return list; + } + if (nFieldFields > 0 && BridJ.warnStructFields) { + BridJ.warning("Struct " + structClass.getName() + " has " + nFieldFields + " struct fields implemented as Java fields, which won't give the best performance and might require counter-intuitive calls to BridJ.readFromNative / .writeToNative. Please consider using JNAerator to generate your struct instead, or use BRIDJ_WARN_STRUCT_FIELDS=0 or -Dbridj.warnStructFields=false to mute this warning."); } - protected static StructFieldDeclaration fromField(java.lang.reflect.Field getter) { - StructFieldDeclaration field = fromMember((Member) getter); + return list; + } + + protected static String nameForMember( ResolvedMember member ) { + String name = member.getName(); + if (name.matches("get[A-Z].*")) { + return Character.toLowerCase(name.charAt(3)) + name.substring(4); + } else if ( name.matches("set[A-Z].*")) { + return Character.toLowerCase(name.charAt(3)) + name.substring(4); + } else { + return name; + } + } + + protected static StructFieldDeclaration fromField(ResolvedField getter) { + StructFieldDeclaration field = fromMember(getter); field.desc.field = getter; - field.desc.valueType = getter.getGenericType(); - field.valueClass = getter.getType(); + field.desc.valueType = getter.getType(); + field.valueClass = getter.getType().getErasedType(); return field; } - protected static StructFieldDeclaration fromGetter(Method getter) { - StructFieldDeclaration field = fromMember((Member) getter); - field.desc.getter = getter; - field.desc.valueType = getter.getGenericReturnType(); - field.valueClass = getter.getReturnType(); - return field; + protected static StructFieldDeclaration fromGetter(ResolvedMethod getter) { + StructFieldDeclaration field = fromMember(getter); + field.desc.getter = getter; + field.desc.valueType = getter.getReturnType(); + field.valueClass = getter.getReturnType().getErasedType(); + return field; } - private static StructFieldDeclaration fromMember(Member member) { + private static StructFieldDeclaration fromMember(ResolvedMember member) { StructFieldDeclaration field = new StructFieldDeclaration(); - field.declaringClass = member.getDeclaringClass(); + field.declaringClass = member.getRawMember().getDeclaringClass(); String name = member.getName(); if (name.matches("get[A-Z].*")) { @@ -138,11 +148,10 @@ private static StructFieldDeclaration fromMember(Member member) { field.desc.name = name; - AnnotatedElement getter = (AnnotatedElement) member; - Field fil = getter.getAnnotation(Field.class); - Bits bits = getter.getAnnotation(Bits.class); - Alignment alignment = getter.getAnnotation(Alignment.class); - Array arr = getter.getAnnotation(Array.class); + Field fil = member.get(Field.class); + Bits bits = member.get(Bits.class); + Alignment alignment = member.get(Alignment.class); + Array arr = member.get(Array.class); if (fil != null) { field.index = fil.value(); //field.byteOffset = fil.offset(); @@ -166,8 +175,8 @@ private static StructFieldDeclaration fromMember(Member member) { field.desc.arrayLength = length; field.desc.isArray = true; } - field.desc.isCLong = isAnnotationPresent(org.bridj.ann.CLong.class, getter); - field.desc.isSizeT = isAnnotationPresent(org.bridj.ann.Ptr.class, getter); + field.desc.isCLong = member.get(org.bridj.ann.CLong.class) != null; + field.desc.isSizeT = member.get(org.bridj.ann.Ptr.class) != null; return field; } } diff --git a/src/main/java/org/bridj/StructFieldDescription.java b/src/main/java/org/bridj/StructFieldDescription.java index 4f4c2759..e2729eb3 100644 --- a/src/main/java/org/bridj/StructFieldDescription.java +++ b/src/main/java/org/bridj/StructFieldDescription.java @@ -55,6 +55,10 @@ import org.bridj.util.DefaultParameterizedType; import org.bridj.util.Utils; +import com.fasterxml.classmate.ResolvedType; +import com.fasterxml.classmate.members.ResolvedField; +import com.fasterxml.classmate.members.ResolvedMethod; + /** * Internal metadata on a struct field */ @@ -69,9 +73,9 @@ public class StructFieldDescription { public long bitMask = -1; public boolean isArray, isNativeObject; public Type nativeTypeOrPointerTargetType; - public java.lang.reflect.Field field; + public ResolvedField field; Type valueType; - Method getter; + ResolvedMethod getter; String name; boolean isCLong, isSizeT; @@ -90,7 +94,12 @@ static Type resolveType(Type tpe, Type structType) { } Type ret; - if (tpe instanceof ParameterizedType) { + if (tpe instanceof ResolvedType ) { + ResolvedType rt = (ResolvedType)tpe; + // TODO: what do we do here? + ret = tpe; + } + else if (tpe instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) tpe; Type[] actualTypeArguments = pt.getActualTypeArguments(); Type[] resolvedActualTypeArguments = new Type[actualTypeArguments.length]; @@ -178,8 +187,18 @@ static StructFieldDescription aggregateDeclarations(Type structType, List... params ) { + METHODS: for( ResolvedMethod method : methods ) { + if( !name.equals(method.getName()) ) continue METHODS; + if( params.length != method.getArgumentCount()) continue METHODS; + for( int i = 0; i < params.length; i++ ) { + if( !method.getArgumentType(i).isInstanceOf(params[i])) continue METHODS; + } + return method; + } + return null; + } + +} diff --git a/src/main/java/org/bridj/util/Types.java b/src/main/java/org/bridj/util/Types.java new file mode 100644 index 00000000..4279c342 --- /dev/null +++ b/src/main/java/org/bridj/util/Types.java @@ -0,0 +1,46 @@ +package org.bridj.util; + +import com.fasterxml.classmate.AnnotationConfiguration; +import com.fasterxml.classmate.AnnotationConfiguration.StdConfiguration; +import com.fasterxml.classmate.AnnotationInclusion; +import com.fasterxml.classmate.MemberResolver; +import com.fasterxml.classmate.ResolvedType; +import com.fasterxml.classmate.ResolvedTypeWithMembers; +import com.fasterxml.classmate.TypeResolver; + +/** + * Static utilities for resolving types. + * + * @author Christian Trimble + * + */ +public class Types { + static final TypeResolver resolver = new TypeResolver(); + + public static TypeResolver getResolver() { + return resolver; + } + + public static ResolvedTypeWithMembers resolveTypeWithInstanceMethods( Class unresolved ) { + return resolveTypeWithInstanceMethods(resolver.resolve(unresolved)); + } + + public static ResolvedTypeWithMembers resolveTypeWithInstanceMethods( ResolvedType resolvedType ) { + MemberResolver mr = new MemberResolver(resolver); + mr.setMethodFilter(method->!method.isStatic()); + mr.setFieldFilter(field->!field.isStatic()); + AnnotationConfiguration annConfig = new AnnotationConfiguration.StdConfiguration(AnnotationInclusion.INCLUDE_BUT_DONT_INHERIT); + return mr.resolve(resolvedType, annConfig, null); + } + + public static ResolvedTypeWithMembers resolveTypeWithNativeMethods( Class unresolved ) { + return resolveTypeWithNativeMethods(resolver.resolve(unresolved)); + } + public static ResolvedTypeWithMembers resolveTypeWithNativeMethods( ResolvedType resolvedType ) { + MemberResolver mr = new MemberResolver(resolver); + mr.setMethodFilter(method->method.isNative()); + mr.setFieldFilter(field->false); + AnnotationConfiguration annConfig = new AnnotationConfiguration.StdConfiguration(AnnotationInclusion.INCLUDE_BUT_DONT_INHERIT); + return mr.resolve(resolvedType, annConfig, null); + } +} diff --git a/src/main/java/org/bridj/util/Utils.java b/src/main/java/org/bridj/util/Utils.java index a9c3b3b3..6087bbd8 100644 --- a/src/main/java/org/bridj/util/Utils.java +++ b/src/main/java/org/bridj/util/Utils.java @@ -48,6 +48,8 @@ import java.nio.LongBuffer; import java.nio.ShortBuffer; +import com.fasterxml.classmate.ResolvedType; + /** * Miscellaneous utility methods. * @@ -162,6 +164,9 @@ public static Class getClass(Type type) { if (type instanceof Class) { return (Class) type; } + if (type instanceof ResolvedType ) { + return (Class)((ResolvedType) type).getErasedType(); + } if (type instanceof ParameterizedType) { return getClass(((ParameterizedType) type).getRawType()); } diff --git a/src/main/velocity/org/bridj/PointerIO.java b/src/main/velocity/org/bridj/PointerIO.java index 5afa0aa8..7333159b 100644 --- a/src/main/velocity/org/bridj/PointerIO.java +++ b/src/main/velocity/org/bridj/PointerIO.java @@ -30,6 +30,8 @@ */ package org.bridj; +import com.fasterxml.classmate.ResolvedType; + import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.*; @@ -94,6 +96,8 @@ public Type getTargetType() { static Class getClass(Type type) { if (type instanceof Class) return (Class)type; + if (type instanceof ResolvedType) + return ((ResolvedType)type).getErasedType(); if (type instanceof ParameterizedType) return getClass(((ParameterizedType)type).getRawType()); return null; @@ -149,12 +153,24 @@ public static

PointerIO

getInstance(Type type) { if (type == null) return null; - PointerIO io = ios.get(type); + PointerIO io = null; + + if( type instanceof ResolvedType ) { + io = ios.get(((ResolvedType) type).getErasedType()); + } + if( io == null ) { + io = ios.get(type); + } if (io == null) { final Class cl = Utils.getClass(type); if (cl != null) { - if (cl == Pointer.class) - io = getPointerInstance(((ParameterizedType)type).getActualTypeArguments()[0]); + if (cl == Pointer.class) { + if( type instanceof ResolvedType ) { + io = getPointerInstance(((ResolvedType)type).getTypeParameters().get(0)); + } else { + io = getPointerInstance(((ParameterizedType)type).getActualTypeArguments()[0]); + } + } else if (StructObject.class.isAssignableFrom(cl)) io = getInstance(StructIO.getInstance((Class)cl, type)); else if (Callback.class.isAssignableFrom(cl)) @@ -162,7 +178,11 @@ else if (Callback.class.isAssignableFrom(cl)) else if (NativeObject.class.isAssignableFrom(cl)) io = new CommonPointerIOs.NativeObjectPointerIO(type); else if (IntValuedEnum.class.isAssignableFrom(cl)) { - if (type instanceof ParameterizedType) { + if (type instanceof ResolvedType) { + ResolvedType enumType = ((ResolvedType)type).getTypeParameters().get(0); + io = new CommonPointerIOs.IntValuedEnumPointerIO(enumType.getErasedType()); + } + else if (type instanceof ParameterizedType) { Type enumType = ((ParameterizedType)type).getActualTypeArguments()[0]; if (enumType instanceof Class) io = new CommonPointerIOs.IntValuedEnumPointerIO((Class)enumType); diff --git a/src/main/velocity/org/bridj/StructIO.java b/src/main/velocity/org/bridj/StructIO.java index 7a6394c7..e07e7a6b 100644 --- a/src/main/velocity/org/bridj/StructIO.java +++ b/src/main/velocity/org/bridj/StructIO.java @@ -131,14 +131,14 @@ public final void writeFieldsToNative(StructObject struct) { if (fd.isArray) continue; - Object value = fd.field.get(struct); + Object value = fd.field.getRawMember().get(struct); if (value instanceof NativeObject) {//fd.isNativeObject) { if (value != null) BridJ.writeToNative((NativeObject)value); continue; } Pointer ptr = struct.peer.offset(fd.byteOffset); - Type tpe = fd.isNativeObject || fd.isArray ? fd.nativeTypeOrPointerTargetType : fd.field.getGenericType(); + Type tpe = fd.isNativeObject || fd.isArray ? fd.nativeTypeOrPointerTargetType : fd.field.getType(); ptr = ptr.as(tpe); ptr = fixIntegralTypeIOToMatchLength(ptr, fd.byteLength, fd.arrayLength); @@ -166,7 +166,7 @@ public final void readFieldsFromNative(StructObject struct) { continue; Pointer ptr = struct.peer.offset(fd.byteOffset); - Type tpe = fd.isNativeObject || fd.isArray ? fd.nativeTypeOrPointerTargetType : fd.field.getGenericType(); + Type tpe = fd.isNativeObject || fd.isArray ? fd.nativeTypeOrPointerTargetType : fd.field.getType(); ptr = ptr.as(tpe); ptr = fixIntegralTypeIOToMatchLength(ptr, fd.byteLength, fd.arrayLength); Object value; @@ -176,7 +176,7 @@ public final void readFieldsFromNative(StructObject struct) { } else { value = ptr.get(); } - fd.field.set(struct, value); + fd.field.getRawMember().set(struct, value); if (value instanceof NativeObject) {//if (fd.isNativeObject) { if (value != null) diff --git a/src/test/java/org/bridj/StructFieldDeclarationTest.java b/src/test/java/org/bridj/StructFieldDeclarationTest.java new file mode 100644 index 00000000..45102fbc --- /dev/null +++ b/src/test/java/org/bridj/StructFieldDeclarationTest.java @@ -0,0 +1,30 @@ +package org.bridj; + +import java.util.List; + +import org.bridj.ann.Field; +import org.junit.Test; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.CoreMatchers.*; + +public class StructFieldDeclarationTest { + + public static class BasicFieldStruct extends StructObject { + @Field(0) + public int intField; + } + + public static class StaticNestedStruct extends StructObject { + @Field(0) + public BasicFieldStruct structField; + } + + public static class GenericNestedStruct extends StructObject { + @Field(0) + public S structField; + } + + public static class NestedStructOfBasicFieldStruct + extends GenericNestedStruct {} + +} diff --git a/src/test/java/org/bridj/StructGenericsTest.java b/src/test/java/org/bridj/StructGenericsTest.java new file mode 100644 index 00000000..1539626e --- /dev/null +++ b/src/test/java/org/bridj/StructGenericsTest.java @@ -0,0 +1,51 @@ +package org.bridj; + +import static org.bridj.Pointer.getPointer; + +import org.bridj.ann.Field; +import org.junit.Test; +import org.hamcrest.CoreMatchers; +import org.hamcrest.MatcherAssert; + +public class StructGenericsTest { + public static abstract class AbstractStructField> extends StructObject { + @Field(0) + public S getStruct() { + return io.getNativeObjectField(this, 0); + } + + public T setStruct( S struct ) { + io.setNativeObjectField(this, 0, struct); + return (T)this; + } + } + + public static class StructOfInteger extends StructObject { + @Field(0) + public int getField() { + return io.getIntField(this, 0); + } + + public StructOfInteger setField( int field ) { + io.setIntField(this, 0, field); + return this; + } + } + + public static class NestedStructOfInteger + extends AbstractStructField {} + + @Test + public void shouldSupportNestedStructOfPrimative() { + NestedStructOfInteger s = new NestedStructOfInteger(); + s.setStruct(new StructOfInteger().setField(2)); + BridJ.writeToNative(s); + s = getPointer(s).get(); + MatcherAssert.assertThat(s.getStruct().getField(), CoreMatchers.equalTo(2)); + } + + @Test + public void shouldSupportNestedStructs() { + + } +} diff --git a/src/test/velocity/org/bridj/PointerTest.java b/src/test/velocity/org/bridj/PointerTest.java index de3c191b..9ce7d55e 100644 --- a/src/test/velocity/org/bridj/PointerTest.java +++ b/src/test/velocity/org/bridj/PointerTest.java @@ -675,10 +675,15 @@ public void testUpdateDirectBufferOnNonBufferBoundPointer() { assertTrue(!it.hasNext()); } +#if ($prim.Name == "Pointer") +#set ($rawTypeRef = "?") +#else +#set ($rawTypeRef = $prim.rawTypeRef) +#end @Test public void testPointerTo_${prim.Name}_Values() { // Test pointerToInts(int...) - Pointer<${prim.rawTypeRef}> p = Pointer.pointerTo${prim.CapName}s(${prim.value($v1)}, ${prim.value($v2)}, ${prim.value($v3)}); + Pointer<${rawTypeRef}> p = Pointer.pointerTo${prim.CapName}s(${prim.value($v1)}, ${prim.value($v2)}, ${prim.value($v3)}); assertEquals(${prim.value($v1)}, (${prim.Name})p.get(0)$precisionArg); assertEquals(${prim.value($v2)}, (${prim.Name})p.get(1)$precisionArg); assertEquals(${prim.value($v3)}, (${prim.Name})p.get(2)$precisionArg); @@ -699,7 +704,7 @@ public void testUpdateDirectBufferOnNonBufferBoundPointer() { } @Test public void testPointerTo_${prim.Name}_Value() { - Pointer<${prim.rawTypeRef}> p = Pointer.pointerTo${prim.CapName}(${prim.value($v1)}); + Pointer<${rawTypeRef}> p = Pointer.pointerTo${prim.CapName}(${prim.value($v1)}); assertEquals(${prim.value($v1)}, (${prim.Name})p.get(0)$precisionArg); p = Pointer.pointerTo${prim.CapName}(${prim.rawValue($v1)});