Skip to content

Commit

Permalink
fix varargs on ARM
Browse files Browse the repository at this point in the history
  • Loading branch information
twall committed Feb 7, 2016
1 parent a31715d commit 0d9ab19
Show file tree
Hide file tree
Showing 10 changed files with 88 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Bug Fixes
* [#531](https://github.com/java-native-access/jna/pull/531): Ensure direct-mapped callbacks use the right calling convention - [@twall](https://github.com/twall).
* [#566](https://github.com/java-native-access/jna/pull/566): Fix return type of Native#loadLibrary to match unconstrained generic [@lgoldstein](https://github.com/lgoldstein)
* [#584](https://github.com/java-native-access/jna/pull/584): Promote float varargs to double - [@marco2357](https://github.com/marco2357).
* [#588](https://github.com/java-native-access/jna/pull/588): Fix varargs calls on arm - [@twall](https://github.com/twall).

Release 4.2.1
=============
Expand Down
2 changes: 1 addition & 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="4"/>
<property name="jni.minor" value="0"/>
<property name="jni.revision" value="1"/>
<property name="jni.revision" value="0"/>
<property name="jni.build" value="0"/> <!--${build.number}-->
<property name="jni.version" value="${jni.major}.${jni.minor}.${jni.revision}"/>
<property name="jni.md5" value="1a6047467b59e8748f975e03016ce3d9"/>
Expand Down
Binary file modified lib/native/linux-arm.jar
Binary file not shown.
4 changes: 2 additions & 2 deletions native/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ OS=$(shell uname | sed -e 's/CYGWIN.*/win32/g' \
-e 's/AIX.*/aix/g' \
-e 's/Linux.*/linux/g')

JNA_JNI_VERSION=4.0.1 # auto-generated by ant
CHECKSUM=1a6047467b59e8748f975e03016ce3d9 # auto-generated by ant
JNA_JNI_VERSION=5.0.0 # auto-generated by ant
CHECKSUM=4f72f2799dfee6008a386bc40afd7428 # auto-generated by ant

JAVA_INCLUDES=-I"$(JAVA_HOME)/include" \
-I"$(JAVA_HOME)/include/$(OS)"
Expand Down
6 changes: 5 additions & 1 deletion native/dispatch.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ extern "C" {

#define MASK_CC com_sun_jna_Function_MASK_CC
#define THROW_LAST_ERROR com_sun_jna_Function_THROW_LAST_ERROR
#define USE_VARARGS com_sun_jna_Function_USE_VARARGS

/* Cached class, field and method IDs */
static jclass classObject;
Expand Down Expand Up @@ -435,6 +436,7 @@ dispatch(JNIEnv *env, void* func, jint flags, jobjectArray args,
callconv_t callconv = flags & MASK_CC;
const char* volatile throw_type = NULL;
const char* volatile throw_msg = NULL;
int fixed_args = (flags & USE_VARARGS) >> 7;

nargs = (*env)->GetArrayLength(env, args);

Expand Down Expand Up @@ -609,7 +611,9 @@ dispatch(JNIEnv *env, void* func, jint flags, jobjectArray args,
break;
}

status = ffi_prep_cif(&cif, abi, nargs, return_type, arg_types);
status = fixed_args
? ffi_prep_cif_var(&cif, abi, fixed_args, nargs, return_type, arg_types)
: ffi_prep_cif(&cif, abi, nargs, return_type, arg_types);
if (!ffi_error(env, "Native call setup", status)) {
PSTART();
if ((flags & THROW_LAST_ERROR) != 0) {
Expand Down
8 changes: 5 additions & 3 deletions native/testlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -855,12 +855,14 @@ fillDoubleBuffer(double *buf, int len, double value) {
return len;
}

#include "ffi.h"

EXPORT int32_t
addInt32VarArgs(const char *fmt, ...) {
addVarArgs(const char *fmt, ...) {
va_list ap;
int32_t sum = 0;
va_start(ap, fmt);

while (*fmt) {
switch (*fmt++) {
case 'd':
Expand All @@ -873,7 +875,7 @@ addInt32VarArgs(const char *fmt, ...) {
sum += (int) va_arg(ap, int);
break;
case 'f': // float (promoted to ‘double’ when passed through ‘...’)
case 'F': // double
case 'g': // double
sum += va_arg(ap, double);
break;
default:
Expand Down
19 changes: 16 additions & 3 deletions src/com/sun/jna/Function.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ public interface PostCallRead {
private static final int MASK_CC = 0x3F;
/** Whether to throw an exception if last error is non-zero after call. */
public static final int THROW_LAST_ERROR = 0x40;
/** Mask for number of fixed args (1-3) for varargs calls. */
public static final int USE_VARARGS = 0x180;

static final Integer INTEGER_TRUE = Integer.valueOf(-1);
static final Integer INTEGER_FALSE = Integer.valueOf(0);
Expand Down Expand Up @@ -293,10 +295,10 @@ Object invoke(Method invokingMethod, Class<?>[] paramTypes, Class<?> returnType,

TypeMapper mapper = (TypeMapper)options.get(Library.OPTION_TYPE_MAPPER);
boolean allowObjects = Boolean.TRUE.equals(options.get(Library.OPTION_ALLOW_OBJECTS));
boolean isVarArgs = args.length > 0 && invokingMethod != null ? isVarArgs(invokingMethod) : false;
int fixedArgs = args.length > 0 && invokingMethod != null ? fixedArgs(invokingMethod) : 0;
for (int i=0; i < args.length; i++) {
Class<?> paramType = invokingMethod != null
? (isVarArgs && i >= paramTypes.length-1
? (fixedArgs > 0 && i >= paramTypes.length-1
? paramTypes[paramTypes.length-1].getComponentType()
: paramTypes[i])
: null;
Expand All @@ -316,7 +318,7 @@ Object invoke(Method invokingMethod, Class<?>[] paramTypes, Class<?> returnType,
}
}

Object result = invoke(args, nativeReturnType, allowObjects);
Object result = invoke(args, nativeReturnType, allowObjects, fixedArgs);
// Convert the result to a custom value/type if appropriate
if (resultConverter != null) {
FromNativeContext context;
Expand Down Expand Up @@ -362,7 +364,13 @@ Object invoke(Method invokingMethod, Class<?>[] paramTypes, Class<?> returnType,

/* @see NativeLibrary#NativeLibrary(String,String,long,Map) implementation */
Object invoke(Object[] args, Class<?> returnType, boolean allowObjects) {
return invoke(args, returnType, allowObjects, 0);
}

/* @see NativeLibrary#NativeLibrary(String,String,long,Map) implementation */
Object invoke(Object[] args, Class<?> returnType, boolean allowObjects, int fixedArgs) {
Object result = null;
int callFlags = this.callFlags | ((fixedArgs & 0x3) << 7);
if (returnType == null || returnType==void.class || returnType==Void.class) {
Native.invokeVoid(peer, callFlags, args);
result = null;
Expand Down Expand Up @@ -752,6 +760,11 @@ static boolean isVarArgs(Method m) {
return IS_VARARGS.isVarArgs(m);
}

/** Varargs are only supported on 1.5+. */
static int fixedArgs(Method m) {
return IS_VARARGS.fixedArgs(m);
}

private static class NativeMappedArray extends Memory implements PostCallRead {
private final NativeMapped[] original;
public NativeMappedArray(NativeMapped[] arg) {
Expand Down
13 changes: 13 additions & 0 deletions src/com/sun/jna/VarArgsChecker.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ boolean isVarArgs(Method m) {
return m.isVarArgs();
}

int fixedArgs(Method m) {
// In Java, final argument contains all "varargs"
return m.getParameterTypes().length - 1;
}
}

/**
Expand All @@ -37,6 +41,9 @@ boolean isVarArgs(Method m) {
return false;
}

int fixedArgs(Method m) {
return 0;
}
}

/**
Expand Down Expand Up @@ -68,4 +75,10 @@ static VarArgsChecker create() {
*/
abstract boolean isVarArgs(Method m);

/**
* If variadic, returns the number of fixed arguments to the method.
* @param m Method to be checked
* @return Number of fixed arguments if the given method takes a variable number of arguments, zero otherwise.
*/
abstract int fixedArgs(Method m);
}
36 changes: 33 additions & 3 deletions test/com/sun/jna/VarArgsCheckerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public void testNoVarArgs() throws Exception {
final Method toCheckForVarArgs = VarArgsCheckerTest.class.getMethod("testNoVarArgs", new Class[0]);
final boolean res = sut.isVarArgs(toCheckForVarArgs);
// no matter if JVM 1.4 or 1.5+, the result should always be false
assertFalse(res);
assertFalse("Method should not be detected as varargs", res);
}

public void testVarArgsExist() throws Exception {
Expand All @@ -25,10 +25,40 @@ public void testVarArgsExist() throws Exception {
final boolean res = sut.isVarArgs(toCheckForVarArgs);
// this test has to run with Java 1.5+, because this has a method with
// varargs. So the result has to be true.
assertTrue(res);
assertTrue("Method should be detected as varargs", res);
}

public void testFixedArgsOne() throws Exception {
final VarArgsChecker checker = VarArgsChecker.create();
final Method method = VarArgsCheckerTest.class.getMethod("methodWithOneFixedArg", new Class[]{String.class, Object[].class});
final int res = checker.fixedArgs(method);
// this test has to run with Java 1.5+, because this has a method with
// varargs. So the result has to be true.
assertEquals("Wrong number of fixed args", 1, res);
}

public void testFixedArgsTwo() throws Exception {
final VarArgsChecker checker = VarArgsChecker.create();
final Method method = VarArgsCheckerTest.class.getMethod("methodWithTwoFixedArgs", new Class[]{String.class, String.class, Object[].class});
final int res = checker.fixedArgs(method);
// this test has to run with Java 1.5+, because this has a method with
// varargs. So the result has to be true.
assertEquals("Wrong number of fixed args", 2, res);
}

public void methodWithVarArgs(String... args) {
System.out.println();
// nothing to do
}

public void methodWithOneFixedArg(String fmt, Object... args) {
// nothing to do
}

public void methodWithTwoFixedArgs(String fmt, String fmt2, Object... args) {
// nothing to do
}

public static void main(String[] args) {
junit.textui.TestRunner.run(VarArgsCheckerTest.class);
}
}
24 changes: 12 additions & 12 deletions test/com/sun/jna/VarArgsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ protected List<String> getFieldOrder() {
return Arrays.asList("magic");
}
}
public int addInt32VarArgs(String fmt, Number... args);
public int addVarArgs(String fmt, Number... args);
public String returnStringVarArgs(String fmt, Object... args);
public void modifyStructureVarArgs(String fmt, Object arg1, Object... args);
}
Expand All @@ -45,32 +45,32 @@ protected void tearDown() {
public void testIntVarArgs() {
int arg1 = 1;
int arg2 = 2;
assertEquals("VarArgs not added correctly", arg1 + arg2,
lib.addInt32VarArgs("dd", Integer.valueOf(arg1), Integer.valueOf(arg2)));
assertEquals("32-bit integer varargs not added correctly", arg1 + arg2,
lib.addVarArgs("dd", Integer.valueOf(arg1), Integer.valueOf(arg2)));
}
public void testShortVarArgs() {
short arg1 = 1;
short arg2 = 2;
assertEquals("VarArgs not added correctly", arg1 + arg2,
lib.addInt32VarArgs("dd", Short.valueOf(arg1), Short.valueOf(arg2)));
assertEquals("16-bit integer varargs not added correctly", arg1 + arg2,
lib.addVarArgs("dd", Short.valueOf(arg1), Short.valueOf(arg2)));
}
public void testLongVarArgs() {
short arg1 = 1;
short arg2 = 2;
assertEquals("VarArgs not added correctly", arg1 + arg2,
lib.addInt32VarArgs("ll", Long.valueOf(arg1), Long.valueOf(arg2)));
assertEquals("64-bit integer varargs not added correctly", arg1 + arg2,
lib.addVarArgs("ll", Long.valueOf(arg1), Long.valueOf(arg2)));
}
public void testFloatVarArgs() {
float arg1 = 1;
float arg2 = 2;
assertEquals("VarArgs not added correctly", (int)arg1 + (int)arg2,
lib.addInt32VarArgs("ff", Float.valueOf(arg1), Float.valueOf(arg2)));
assertEquals("float varargs not added correctly", (int)arg1 + (int)arg2,
lib.addVarArgs("ff", Float.valueOf(arg1), Float.valueOf(arg2)));
}
public void testDoubleVarArgs() {
double arg1 = 1;
double arg2 = 2;
assertEquals("VarArgs not added correctly", (int)arg1 + (int)arg2,
lib.addInt32VarArgs("FF", Double.valueOf(arg1), Double.valueOf(arg2)));
assertEquals("double varargs not added correctly", (int)arg1 + (int)arg2,
lib.addVarArgs("gg", Double.valueOf(arg1), Double.valueOf(arg2)));
}
public void testStringVarArgs() {
Object[] args = new Object[] { "Test" };
Expand All @@ -81,7 +81,7 @@ public void testStringVarArgs() {
public void testAppendNullToVarargs() {
Number[] args = new Number[] { Integer.valueOf(1) };
assertEquals("No trailing NULL was appended to varargs list",
1, lib.addInt32VarArgs("dd", args));
1, lib.addVarArgs("dd", args));
}

public void testModifyStructureInVarargs() {
Expand Down

0 comments on commit 0d9ab19

Please sign in to comment.