Skip to content

Commit

Permalink
Merge pull request java-native-access#919 from matthiasblaesing/direc…
Browse files Browse the repository at this point in the history
…tmapping

Improve/Fix direct mapping support
  • Loading branch information
matthiasblaesing authored Feb 5, 2018
2 parents 5b11de5 + 6af211f commit 2856d95
Show file tree
Hide file tree
Showing 47 changed files with 743 additions and 75 deletions.
9 changes: 8 additions & 1 deletion build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
<!-- jnidispatch library release version -->
<property name="jni.major" value="5"/>
<property name="jni.minor" value="2"/>
<property name="jni.revision" value="0"/>
<property name="jni.revision" value="1"/>
<property name="jni.build" value="0"/> <!--${build.number}-->
<property name="jni.version" value="${jni.major}.${jni.minor}.${jni.revision}"/>
<property name="jni.md5" value="74e8f8e397c43487738c5c1f1363498b"/>
Expand Down Expand Up @@ -1015,11 +1015,17 @@ osname=macosx;processor=x86;processor=x86-64;processor=ppc
<include name="*testlib-jar*"/>
</fileset>
</copy>
<copy todir="${test.classes}">
<fileset dir="${test.src}">
<include name="**/data/**" />
</fileset>
</copy>
<!-- Create a jar for easy movement of tests, and jar load test -->
<jar jarfile="${build}/${testjar}">
<fileset dir="${test.classes}">
<patternset refid="jar-compiled"/>
<include name="**/*testlib-jar*"/>
<include name="**/data/**" />
</fileset>
<manifest>
<attribute name="permissions" value="all-permissions"/>
Expand Down Expand Up @@ -1115,6 +1121,7 @@ osname=macosx;processor=x86;processor=x86-64;processor=ppc
<propertyref prefix="java.awt.headless"/>
</propertyset>
<junit fork="${test.fork}" failureproperty="testfailure" tempdir="${build}">
<!--<jvmarg value="-Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y" />-->
<!-- optionally run headless -->
<syspropertyset refid="headless"/>
<!-- avoid VM conflicts with JNA protected mode -->
Expand Down
Binary file modified lib/native/aix-ppc.jar
Binary file not shown.
Binary file modified lib/native/aix-ppc64.jar
Binary file not shown.
Binary file modified lib/native/android-aarch64.jar
Binary file not shown.
Binary file modified lib/native/android-arm.jar
Binary file not shown.
Binary file modified lib/native/android-armv7.jar
Binary file not shown.
Binary file modified lib/native/android-mips.jar
Binary file not shown.
Binary file modified lib/native/android-mips64.jar
Binary file not shown.
Binary file modified lib/native/android-x86-64.jar
Binary file not shown.
Binary file modified lib/native/android-x86.jar
Binary file not shown.
Binary file modified lib/native/darwin.jar
Binary file not shown.
Binary file modified lib/native/freebsd-x86-64.jar
Binary file not shown.
Binary file modified lib/native/freebsd-x86.jar
Binary file not shown.
Binary file modified lib/native/linux-aarch64.jar
Binary file not shown.
Binary file modified lib/native/linux-arm.jar
Binary file not shown.
Binary file modified lib/native/linux-armel.jar
Binary file not shown.
Binary file modified lib/native/linux-mips64el.jar
Binary file not shown.
Binary file modified lib/native/linux-ppc.jar
Binary file not shown.
Binary file modified lib/native/linux-ppc64le.jar
Binary file not shown.
Binary file modified lib/native/linux-s390x.jar
Binary file not shown.
Binary file modified lib/native/linux-x86-64.jar
Binary file not shown.
Binary file modified lib/native/linux-x86.jar
Binary file not shown.
Binary file modified lib/native/openbsd-x86-64.jar
Binary file not shown.
Binary file modified lib/native/openbsd-x86.jar
Binary file not shown.
Binary file modified lib/native/sunos-sparc.jar
Binary file not shown.
Binary file modified lib/native/sunos-sparcv9.jar
Binary file not shown.
Binary file modified lib/native/sunos-x86-64.jar
Binary file not shown.
Binary file modified lib/native/win32-x86-64.jar
Binary file not shown.
Binary file modified lib/native/win32-x86.jar
Binary file not shown.
44 changes: 34 additions & 10 deletions native/dispatch.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ static jclass classIntegerType;
static jclass classPointerType;
static jclass classJNIEnv;
static jclass class_ffi_callback;
static jclass classFromNativeConverter;

static jmethodID MID_Class_getComponentType;
static jmethodID MID_Object_toString;
Expand Down Expand Up @@ -224,7 +225,7 @@ static jmethodID MID_CallbackReference_getNativeString;
static jmethodID MID_CallbackReference_initializeThread;
static jmethodID MID_NativeMapped_toNative;
static jmethodID MID_WString_init;
static jmethodID MID_ToNativeConverter_nativeType;
static jmethodID MID_FromNativeConverter_nativeType;
static jmethodID MID_ffi_callback_invoke;

static jfieldID FID_Boolean_value;
Expand Down Expand Up @@ -1059,7 +1060,11 @@ get_java_type(JNIEnv* env, jclass cls) {

jlong
getIntegerTypeValue(JNIEnv* env, jobject obj) {
return (*env)->GetLongField(env, obj, FID_IntegerType_value);
if(obj == NULL) {
return 0;
} else {
return (*env)->GetLongField(env, obj, FID_IntegerType_value);
}
}

void*
Expand Down Expand Up @@ -1143,10 +1148,10 @@ getNativeType(JNIEnv* env, jclass cls) {
MID_Native_nativeType, cls);
}

void*
getFFITypeTypeMapped(JNIEnv* env, jobject converter) {
return L2A((*env)->CallStaticLongMethod(env, converter,
MID_ToNativeConverter_nativeType));
jclass
getNativeTypeMapped(JNIEnv* env, jobject converter) {
return (*env)->CallObjectMethod(env, converter,
MID_FromNativeConverter_nativeType);
}

void
Expand Down Expand Up @@ -1905,10 +1910,20 @@ dispatch_direct(ffi_cif* cif, void* volatile resp, void** argp, void *cdata) {
case CVT_TYPE_MAPPER_STRING:
case CVT_TYPE_MAPPER_WSTRING:
{
int jtype = (data->rflag == CVT_TYPE_MAPPER_STRING
? 'c' : (data->rflag == CVT_TYPE_MAPPER_WSTRING
? 'w' : get_java_type_from_ffi_type(data->cif.rtype)));
fromNativeTypeMapped(env, data->from_native, resp, jtype, data->cif.rtype->size,
int jtype;
if(data->rflag == CVT_TYPE_MAPPER_STRING) {
jtype = 'c';
} else if (data->rflag == CVT_TYPE_MAPPER_WSTRING) {
jtype = 'w';
} else {
jclass returnClass = getNativeTypeMapped(env, data->from_native);
jtype = get_java_type(env, returnClass);
if(jtype == -1) {
jtype = get_java_type_from_ffi_type(data->cif.rtype);
}
}

fromNativeTypeMapped(env, data->from_native, resp, jtype, data->cif.rtype->size,
data->closure_method, oldresp, data->encoding);
}
break;
Expand Down Expand Up @@ -2983,6 +2998,15 @@ Java_com_sun_jna_Native_initIDs(JNIEnv *env, jclass cls) {
throwByName(env, EUnsatisfiedLink,
"Can't obtain invoke method from class com.sun.jna.Native$ffi_callback");
}
else if (!LOAD_CREF(env, FromNativeConverter, "com/sun/jna/FromNativeConverter")) {
throwByName(env, EUnsatisfiedLink,
"Can't obtain class com.sun.jna.FromNativeConverter");
}
else if (!LOAD_MID(env, MID_FromNativeConverter_nativeType, classFromNativeConverter,
"nativeType", "()Ljava/lang/Class;")) {
throwByName(env, EUnsatisfiedLink,
"Can't obtain method nativeType for class com.sun.jna.FromNativeConverter");
}
// Initialize type fields within Structure.FFIType
else {
#define CFFITYPE "com/sun/jna/Structure$FFIType$FFITypes"
Expand Down
10 changes: 1 addition & 9 deletions src/com/sun/jna/Native.java
Original file line number Diff line number Diff line change
Expand Up @@ -1511,15 +1511,7 @@ static String replace(String s1, String s2, String str) {
private static final int CVT_JNIENV = 27;

private static int getConversion(Class<?> type, TypeMapper mapper, boolean allowObjects) {
if (type == Boolean.class) type = boolean.class;
else if (type == Byte.class) type = byte.class;
else if (type == Short.class) type = short.class;
else if (type == Character.class) type = char.class;
else if (type == Integer.class) type = int.class;
else if (type == Long.class) type = long.class;
else if (type == Float.class) type = float.class;
else if (type == Double.class) type = double.class;
else if (type == Void.class) type = void.class;
if (type == Void.class) type = void.class;

if (mapper != null) {
FromNativeConverter fromNative = mapper.getFromNativeConverter(type);
Expand Down
227 changes: 227 additions & 0 deletions test/com/sun/jna/ArgumentsMarshalNullableTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
/* Copyright (c) 2018 Matthias Bläsing, All Rights Reserved
*
* The contents of this file is dual-licensed under 2
* alternative Open Source/Free licenses: LGPL 2.1 or later and
* Apache License 2.0. (starting with JNA version 4.0.0).
*
* You can freely decide which license you want to apply to
* the project.
*
* You may obtain a copy of the LGPL License at:
*
* http://www.gnu.org/licenses/licenses.html
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "LGPL2.1".
*
* You may obtain a copy of the Apache License at:
*
* http://www.apache.org/licenses/
*
* A copy is also included in the downloadable source code package
* containing JNA, in file "AL2.0".
*/
package com.sun.jna;


import java.util.Collections;
import junit.framework.TestCase;

/**
* Test that all method call options for native calls work with NULL values
* and result in deterministic behaviour and not in a JVM crash.
*/
public class ArgumentsMarshalNullableTest extends TestCase {
public static class Int32Integer extends IntegerType {

public Int32Integer() {
super(4);
}

public Int32Integer(long value) {
super(4, value);
}

}


public static class Int32NativeMapped implements NativeMapped {
private int value;

public Int32NativeMapped() {};

public Int32NativeMapped(int value) {
this.value = value;
}

public Object fromNative(Object nativeValue, FromNativeContext context) {
if(nativeValue instanceof Integer) {
return new Int32NativeMapped((Integer) nativeValue);
} else {
return null;
}
}

public Object toNative() {
return value;
}

public Class<?> nativeType() {
return int.class;
}

@Override
public String toString() {
return "Int32NativeMapped{" + "value=" + value + '}';
}

@Override
public int hashCode() {
int hash = 7;
hash = 59 * hash + this.value;
return hash;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Int32NativeMapped other = (Int32NativeMapped) obj;
if (this.value != other.value) {
return false;
}
return true;
}
}

public static class Int32 {
public static final FromNativeConverter fromNative = new FromNativeConverter() {
public Object fromNative(Object nativeValue, FromNativeContext context) {
if (nativeValue instanceof Integer) {
return new Int32((Integer) nativeValue);
} else {
return null;
}
}

public Class<?> nativeType() {
return int.class;
}
};

public static final ToNativeConverter toNative = new ToNativeConverter() {
public Object toNative(Object value, ToNativeContext context) {
if(value == null) {
return 0;
} else {
return ((Int32) value).value;
}
}

public Class<?> nativeType() {
return int.class;
}
};

private int value;

public Int32() {
}

public Int32(int value) {
this.value = value;
}

@Override
public int hashCode() {
int hash = 7;
hash = 53 * hash + this.value;
return hash;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Int32 other = (Int32) obj;
if (this.value != other.value) {
return false;
}
return true;
}

@Override
public String toString() {
return "Int32{" + "value=" + value + '}';
}
}

public static interface TestLibrary extends Library {
Int32NativeMapped returnInt32Argument(Int32NativeMapped i);
Int32Integer returnInt32Argument(Int32Integer i);
Int32 returnInt32Argument(Int32 i);
}

TestLibrary lib;
@Override
protected void setUp() {
lib = Native.loadLibrary("testlib", TestLibrary.class,
Collections.singletonMap(Library.OPTION_TYPE_MAPPER, new TypeMapper() {
public FromNativeConverter getFromNativeConverter(Class<?> javaType) {
if(javaType == Int32.class) {
return Int32.fromNative;
} else {
return null;
}
}

public ToNativeConverter getToNativeConverter(Class<?> javaType) {
if(javaType == Int32.class) {
return Int32.toNative;
} else {
return null;
}
}
}));
}

@Override
protected void tearDown() {
lib = null;
}


public void testNativeMapped() {
assertEquals("Basic non-null call", new Int32NativeMapped(42), lib.returnInt32Argument(new Int32NativeMapped(42)));
assertEquals("null call", new Int32NativeMapped(0), lib.returnInt32Argument((Int32NativeMapped) null));
}

public void testIntegerType() {
assertEquals("Basic non-null call", new Int32Integer(42), lib.returnInt32Argument(new Int32Integer(42)));
assertEquals("null call", new Int32Integer(0), lib.returnInt32Argument((Int32Integer) null));
}

public void testTypeMapper() {
assertEquals("Basic non-null call", new Int32(42), lib.returnInt32Argument(new Int32(42)));
assertEquals("null call", new Int32(0), lib.returnInt32Argument((Int32) null));
}

public static void main(java.lang.String[] argList) {
junit.textui.TestRunner.run(ArgumentsMarshalNullableTest.class);
}

}
Loading

0 comments on commit 2856d95

Please sign in to comment.