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 {