From 1b22fbf82e07bd59d6905d3642790f072839836e Mon Sep 17 00:00:00 2001 From: Andrew Yefanov <1134togo@gmail.com> Date: Wed, 30 Aug 2017 16:07:01 +0300 Subject: [PATCH 1/2] Add PointerWrapper type You can extend PointerWrapper for typesafe pointer. Example: public interface TestLib { CustomPointer ptr_malloc(@size_t int size); void ptr_free(CustomPointer ptr); class CustomPointer extends PointerWrapper { private CustomPointer(Pointer pointer) { super(pointer); } } } } (cherry picked from commit 98ca7da) --- src/main/java/jnr/ffi/PointerWrapper.java | 33 ++++++++++++ .../PointerWrapperFromNativeConverter.java | 52 +++++++++++++++++++ .../PointerWrapperToNativeConverter.java | 29 +++++++++++ .../ffi/provider/jffi/InvokerTypeMapper.java | 9 +++- src/test/java/jnr/ffi/PointerWrapperTest.java | 44 ++++++++++++++++ 5 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 src/main/java/jnr/ffi/PointerWrapper.java create mode 100644 src/main/java/jnr/ffi/provider/converters/PointerWrapperFromNativeConverter.java create mode 100644 src/main/java/jnr/ffi/provider/converters/PointerWrapperToNativeConverter.java create mode 100644 src/test/java/jnr/ffi/PointerWrapperTest.java diff --git a/src/main/java/jnr/ffi/PointerWrapper.java b/src/main/java/jnr/ffi/PointerWrapper.java new file mode 100644 index 000000000..3d5dc7d51 --- /dev/null +++ b/src/main/java/jnr/ffi/PointerWrapper.java @@ -0,0 +1,33 @@ +package jnr.ffi; + +/** + * A wrapper on {@link jnr.ffi.Pointer} + *

+ * Extend to use as typesafe pointer. Example: + *

+ * {@code
+ * public interface TestLib {
+ *      CustomPointer ptr_malloc(@size_t int size);
+ *      void ptr_free(CustomPointer ptr);
+ *
+ *      class CustomPointer extends PointerWrapper {
+ *          private CustomPointer(Pointer pointer) {
+ *              super(pointer);
+ *          }
+ *      }
+ *  }
+ * }
+ * 
+ */ +public abstract class PointerWrapper { + private final Pointer pointer; + + protected PointerWrapper(Pointer pointer) { + this.pointer = pointer; + } + + public Pointer pointer() { + return pointer; + } +} + diff --git a/src/main/java/jnr/ffi/provider/converters/PointerWrapperFromNativeConverter.java b/src/main/java/jnr/ffi/provider/converters/PointerWrapperFromNativeConverter.java new file mode 100644 index 000000000..02ab93c04 --- /dev/null +++ b/src/main/java/jnr/ffi/provider/converters/PointerWrapperFromNativeConverter.java @@ -0,0 +1,52 @@ +package jnr.ffi.provider.converters; + +import jnr.ffi.Pointer; +import jnr.ffi.PointerWrapper; +import jnr.ffi.mapper.FromNativeContext; +import jnr.ffi.mapper.FromNativeConverter; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +/** + * @author Andrew Yefanov. + * @since 30.08.2017. + */ +public class PointerWrapperFromNativeConverter implements FromNativeConverter { + + private final Constructor constructor; + + private PointerWrapperFromNativeConverter(Constructor constructor) { + this.constructor = constructor; + } + + public static FromNativeConverter getInstance(Class wrapperClass) { + try { + Constructor constructor = wrapperClass.getConstructor(Pointer.class); + if (!constructor.isAccessible()) { + constructor.setAccessible(true); + } + return new PointerWrapperFromNativeConverter(constructor); + } catch (NoSuchMethodException e) { + throw new RuntimeException(wrapperClass.getName() + " has no constructor that accepts jnr.ffi.Pointer"); + } + } + + @Override + public PointerWrapper fromNative(Pointer nativeValue, FromNativeContext context) { + try { + return nativeValue == null ? null : constructor.newInstance(nativeValue); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @Override + public Class nativeType() { + return Pointer.class; + } +} \ No newline at end of file diff --git a/src/main/java/jnr/ffi/provider/converters/PointerWrapperToNativeConverter.java b/src/main/java/jnr/ffi/provider/converters/PointerWrapperToNativeConverter.java new file mode 100644 index 000000000..bf54036ab --- /dev/null +++ b/src/main/java/jnr/ffi/provider/converters/PointerWrapperToNativeConverter.java @@ -0,0 +1,29 @@ +package jnr.ffi.provider.converters; + +import jnr.ffi.Pointer; +import jnr.ffi.PointerWrapper; +import jnr.ffi.mapper.ToNativeContext; +import jnr.ffi.mapper.ToNativeConverter; + +/** + * @author Andrew Yefanov. + * @since 30.08.2017. + */ +public class PointerWrapperToNativeConverter implements ToNativeConverter { + + public static final PointerWrapperToNativeConverter INSTANCE = new PointerWrapperToNativeConverter(); + + public static ToNativeConverter getInstance() { + return INSTANCE; + } + + @Override + public Pointer toNative(PointerWrapper value, ToNativeContext context) { + return value == null ? null : value.pointer(); + } + + @Override + public Class nativeType() { + return Pointer.class; + } +} \ No newline at end of file diff --git a/src/main/java/jnr/ffi/provider/jffi/InvokerTypeMapper.java b/src/main/java/jnr/ffi/provider/jffi/InvokerTypeMapper.java index 42c8cda0c..901847e5d 100644 --- a/src/main/java/jnr/ffi/provider/jffi/InvokerTypeMapper.java +++ b/src/main/java/jnr/ffi/provider/jffi/InvokerTypeMapper.java @@ -20,12 +20,11 @@ import jnr.ffi.NativeLong; import jnr.ffi.Pointer; +import jnr.ffi.PointerWrapper; import jnr.ffi.Struct; import jnr.ffi.annotations.Delegate; import jnr.ffi.byref.ByReference; import jnr.ffi.mapper.*; -import jnr.ffi.mapper.FromNativeType; -import jnr.ffi.mapper.ToNativeType; import jnr.ffi.provider.ParameterFlags; import jnr.ffi.provider.converters.*; @@ -54,6 +53,9 @@ public FromNativeConverter getFromNativeConverter(SignatureType signatureType, F } else if (Struct.class.isAssignableFrom(signatureType.getDeclaredType())) { return structResultConverterFactory.get(signatureType.getDeclaredType().asSubclass(Struct.class), fromNativeContext); + } else if (PointerWrapper.class.isAssignableFrom(signatureType.getDeclaredType())) { + return PointerWrapperFromNativeConverter.getInstance(signatureType.getDeclaredType()); + } else if (closureManager != null && isDelegate(signatureType.getDeclaredType())) { return ClosureFromNativeConverter.getInstance(fromNativeContext.getRuntime(), signatureType, classLoader, this); @@ -91,6 +93,9 @@ public ToNativeConverter getToNativeConverter(SignatureType signatureType, ToNat } else if (Struct.class.isAssignableFrom(javaType)) { return StructByReferenceToNativeConverter.getInstance(context); + } else if (PointerWrapper.class.isAssignableFrom(javaType)) { + return PointerWrapperToNativeConverter.getInstance(); + } else if (NativeLong.class.isAssignableFrom(javaType)) { return NativeLongConverter.getInstance(); diff --git a/src/test/java/jnr/ffi/PointerWrapperTest.java b/src/test/java/jnr/ffi/PointerWrapperTest.java new file mode 100644 index 000000000..93146be3e --- /dev/null +++ b/src/test/java/jnr/ffi/PointerWrapperTest.java @@ -0,0 +1,44 @@ +package jnr.ffi; + +import jnr.ffi.mapper.AnnotatedMappedTypeTest; +import jnr.ffi.types.size_t; +import org.junit.BeforeClass; +import org.junit.Test; + +import static junit.framework.TestCase.assertSame; + +/** + * @author Andrew Yefanov. + * @since 30.08.2017. + */ +public class PointerWrapperTest { + private static final class CustomPointer extends PointerWrapper { + private CustomPointer(Pointer pointer) { + super(pointer); + } + } + + public static interface TestLib { + CustomPointer ptr_malloc(@size_t int size); + void ptr_free(AnnotatedMappedTypeTest.CustomPointer ptr); + } + + static AnnotatedMappedTypeTest.TestLib testlib; + static Runtime runtime; + + @BeforeClass + public static void setUpClass() throws Exception { + testlib = TstUtil.loadTestLib(AnnotatedMappedTypeTest.TestLib.class); + runtime = Runtime.getRuntime(testlib); + } + + @Test + public void returnsInstanceOfCorrectClass() { + assertSame(AnnotatedMappedTypeTest.CustomPointer.class, testlib.ptr_malloc(1).getClass()); + } + + @Test public void toNative() { + testlib.ptr_free(testlib.ptr_malloc(1)); + } + +} \ No newline at end of file From c224a08c5c32236be2851f87d2ad029e327c88f9 Mon Sep 17 00:00:00 2001 From: Andrew Yefanov <1134togo@gmail.com> Date: Wed, 30 Aug 2017 16:43:42 +0300 Subject: [PATCH 2/2] Extract declaredType variable to optimize if-else construction --- .../ffi/provider/jffi/InvokerTypeMapper.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/jnr/ffi/provider/jffi/InvokerTypeMapper.java b/src/main/java/jnr/ffi/provider/jffi/InvokerTypeMapper.java index 901847e5d..42b1eb512 100644 --- a/src/main/java/jnr/ffi/provider/jffi/InvokerTypeMapper.java +++ b/src/main/java/jnr/ffi/provider/jffi/InvokerTypeMapper.java @@ -47,25 +47,26 @@ public InvokerTypeMapper(NativeClosureManager closureManager, AsmClassLoader cla public FromNativeConverter getFromNativeConverter(SignatureType signatureType, FromNativeContext fromNativeContext) { FromNativeConverter converter; - if (Enum.class.isAssignableFrom(signatureType.getDeclaredType())) { - return EnumConverter.getInstance(signatureType.getDeclaredType().asSubclass(Enum.class)); + Class declaredType = signatureType.getDeclaredType(); + if (Enum.class.isAssignableFrom(declaredType)) { + return EnumConverter.getInstance(declaredType.asSubclass(Enum.class)); - } else if (Struct.class.isAssignableFrom(signatureType.getDeclaredType())) { - return structResultConverterFactory.get(signatureType.getDeclaredType().asSubclass(Struct.class), fromNativeContext); + } else if (Struct.class.isAssignableFrom(declaredType)) { + return structResultConverterFactory.get(declaredType.asSubclass(Struct.class), fromNativeContext); - } else if (PointerWrapper.class.isAssignableFrom(signatureType.getDeclaredType())) { - return PointerWrapperFromNativeConverter.getInstance(signatureType.getDeclaredType()); + } else if (PointerWrapper.class.isAssignableFrom(declaredType)) { + return PointerWrapperFromNativeConverter.getInstance(declaredType); - } else if (closureManager != null && isDelegate(signatureType.getDeclaredType())) { + } else if (closureManager != null && isDelegate(declaredType)) { return ClosureFromNativeConverter.getInstance(fromNativeContext.getRuntime(), signatureType, classLoader, this); - } else if (NativeLong.class == signatureType.getDeclaredType()) { + } else if (NativeLong.class == declaredType) { return NativeLongConverter.getInstance(); - } else if (String.class == signatureType.getDeclaredType() || CharSequence.class == signatureType.getDeclaredType()) { + } else if (String.class == declaredType || CharSequence.class == declaredType) { return StringResultConverter.getInstance(fromNativeContext); - } else if ((Set.class == signatureType.getDeclaredType() || EnumSet.class == signatureType.getDeclaredType()) && (converter = EnumSetConverter.getFromNativeConverter(signatureType, fromNativeContext)) != null) { + } else if ((Set.class == declaredType || EnumSet.class == declaredType) && (converter = EnumSetConverter.getFromNativeConverter(signatureType, fromNativeContext)) != null) { return converter; } else {