diff --git a/src/main/java/jnr/ffi/Struct.java b/src/main/java/jnr/ffi/Struct.java index f235472cb..811047ee8 100755 --- a/src/main/java/jnr/ffi/Struct.java +++ b/src/main/java/jnr/ffi/Struct.java @@ -783,7 +783,7 @@ public Boolean() { } public final boolean get() { - return (getMemory().getByte(offset()) & 0x1) != 0; + return getMemory().getByte(offset()) != 0; } public final void set(boolean value) { @@ -800,7 +800,7 @@ public WBOOL() { } public final boolean get() { - return (getMemory().getInt(offset()) & 0x1) != 0; + return getMemory().getInt(offset()) != 0; } public final void set(boolean value) { @@ -814,7 +814,7 @@ public BOOL16() { } public final boolean get() { - return (getMemory().getShort(offset()) & 0x1) != 0; + return getMemory().getShort(offset()) != 0; } public final void set(boolean value) { @@ -1657,7 +1657,7 @@ public UnsignedLong(Offset offset) { */ public final long get() { long value = getMemory().getNativeLong(offset()); - final long mask = getRuntime().findType(NativeType.SLONG).size() == 32 ? 0xffffffffL : 0xffffffffffffffffL; + final long mask = getRuntime().findType(NativeType.SLONG).size() == 4 ? 0xffffffffL : 0xffffffffffffffffL; return value < 0 ? (long) ((value & mask) + mask + 1) : value; diff --git a/src/main/java/jnr/ffi/StructLayout.java b/src/main/java/jnr/ffi/StructLayout.java index a1b32cf0b..68ed0b5e8 100644 --- a/src/main/java/jnr/ffi/StructLayout.java +++ b/src/main/java/jnr/ffi/StructLayout.java @@ -312,7 +312,7 @@ protected Boolean(Offset offset) { } public final boolean get(jnr.ffi.Pointer ptr) { - return (ptr.getByte(offset()) & 0x1) != 0; + return ptr.getByte(offset()) != 0; } public final void set(jnr.ffi.Pointer ptr, boolean value) { @@ -333,7 +333,7 @@ protected WBOOL(Offset offset) { } public final boolean get(jnr.ffi.Pointer ptr) { - return (ptr.getInt(offset()) & 0x1) != 0; + return ptr.getInt(offset()) != 0; } public final void set(jnr.ffi.Pointer ptr, boolean value) { diff --git a/src/main/java/jnr/ffi/byref/NumberByReference.java b/src/main/java/jnr/ffi/byref/NumberByReference.java index d3f2a2387..ecf5b81ac 100644 --- a/src/main/java/jnr/ffi/byref/NumberByReference.java +++ b/src/main/java/jnr/ffi/byref/NumberByReference.java @@ -101,7 +101,7 @@ public void fromNative(Runtime runtime, Pointer memory, long offset) { case SLONG: case ULONG: - value = memory.getLong(offset); + value = memory.getNativeLong(offset); break; case SLONGLONG: @@ -146,7 +146,7 @@ public void toNative(Runtime runtime, Pointer memory, long offset) { case SLONG: case ULONG: - memory.putLong(offset, value.longValue()); + memory.putNativeLong(offset, value.longValue()); break; case SLONGLONG: diff --git a/src/main/java/jnr/ffi/provider/jffi/AsmLibraryLoader.java b/src/main/java/jnr/ffi/provider/jffi/AsmLibraryLoader.java index 14052edcb..0f98ba489 100644 --- a/src/main/java/jnr/ffi/provider/jffi/AsmLibraryLoader.java +++ b/src/main/java/jnr/ffi/provider/jffi/AsmLibraryLoader.java @@ -308,7 +308,7 @@ private void generateVarargsInvocation(AsmBuilder builder, Method m, ObjectField mv.iload(slot); mv.i2b(); mv.invokestatic(Byte.class, "valueOf", Byte.class, byte.class); - } else if (parameterTypes[i].equals(char.class)) { + } else if (parameterTypes[i].equals(boolean.class)) { mv.iload(slot); mv.i2b(); mv.invokestatic(Boolean.class, "valueOf", Boolean.class, boolean.class); diff --git a/src/main/java/jnr/ffi/provider/jffi/AsmUtil.java b/src/main/java/jnr/ffi/provider/jffi/AsmUtil.java index 8897390fc..245b76c60 100644 --- a/src/main/java/jnr/ffi/provider/jffi/AsmUtil.java +++ b/src/main/java/jnr/ffi/provider/jffi/AsmUtil.java @@ -103,6 +103,9 @@ public static Class unboxedType(Class boxedType) { } else if (boxedType == Short.class) { return short.class; + } else if (boxedType == Character.class) { + return char.class; + } else if (boxedType == Integer.class) { return int.class; @@ -134,6 +137,8 @@ public static Class boxedType(Class type) { return Byte.class; } else if (type == short.class) { return Short.class; + } else if (type == char.class) { + return Character.class; } else if (type == int.class) { return Integer.class; } else if (type == long.class) { @@ -341,7 +346,11 @@ static void boxValue(AsmBuilder builder, SkinnyMethodAdapter mv, Class boxedType } else if (Address.class == boxedType) { mv.invokestatic(boxedType, "valueOf", boxedType, unboxedType); - } else if (Number.class.isAssignableFrom(boxedType) && boxedType(unboxedType) == boxedType) { + } else if (Number.class.isAssignableFrom(boxedType) && boxedType(unboxedType) == boxedType) { + mv.invokestatic(boxedType, "valueOf", boxedType, unboxedType); + + } else if (Character.class == boxedType) { + convertPrimitive(mv, unboxedType, char.class); mv.invokestatic(boxedType, "valueOf", boxedType, unboxedType); } else { diff --git a/src/main/java/jnr/ffi/provider/jffi/DefaultInvokerFactory.java b/src/main/java/jnr/ffi/provider/jffi/DefaultInvokerFactory.java index 3a04907b3..b370a825c 100644 --- a/src/main/java/jnr/ffi/provider/jffi/DefaultInvokerFactory.java +++ b/src/main/java/jnr/ffi/provider/jffi/DefaultInvokerFactory.java @@ -158,6 +158,9 @@ private static FunctionInvoker getFunctionInvoker(ResultType resultType) { } else if (Boolean.class.isAssignableFrom(returnType) || boolean.class == returnType) { return BooleanInvoker.INSTANCE; + } else if (Character.class.isAssignableFrom(returnType) || char.class == returnType) { + return CharacterInvoker.INSTANCE; + } else if (Number.class.isAssignableFrom(returnType) || returnType.isPrimitive()) { return new ConvertingInvoker(getNumberResultConverter(resultType), null, new ConvertingInvoker(getNumberDataConverter(resultType.getNativeType()), null, getNumberFunctionInvoker(resultType.getNativeType()))); @@ -241,6 +244,9 @@ static Marshaller getMarshaller(Class type, NativeType nativeType, Collection toNativeConverter; @@ -767,7 +790,7 @@ public Number fromNative(Number nativeValue, FromNativeContext context) { @Override public Number toNative(Number value, ToNativeContext context) { - return value.intValue() & 0xffff; + return value.byteValue() & 0xff; } } @@ -868,7 +891,7 @@ static final class BooleanConverter implements DataConverter { static final DataConverter INSTANCE = new BooleanConverter(); @Override public Boolean fromNative(Number nativeValue, FromNativeContext context) { - return (nativeValue.intValue() & 0x1) != 0; + return nativeValue.longValue() != 0; } @Override diff --git a/src/main/java/jnr/ffi/provider/jffi/NumberUtil.java b/src/main/java/jnr/ffi/provider/jffi/NumberUtil.java index 3ca02c51c..626449243 100644 --- a/src/main/java/jnr/ffi/provider/jffi/NumberUtil.java +++ b/src/main/java/jnr/ffi/provider/jffi/NumberUtil.java @@ -20,6 +20,7 @@ import jnr.ffi.NativeType; import jnr.ffi.provider.SigType; +import org.objectweb.asm.Label; public final class NumberUtil { private NumberUtil() {} @@ -107,11 +108,18 @@ public static void widen(SkinnyMethodAdapter mv, Class from, Class to) { } else if (boolean.class == to && boolean.class != from && isPrimitiveInt(from)) { // Ensure only 0x0 and 0x1 values are used for boolean + Label zero = new Label(); + Label ret = new Label(); + mv.ifeq(zero); mv.iconst_1(); - mv.iand(); + mv.go_to(ret); + mv.label(zero); + mv.iconst_0(); + mv.label(ret); } } + @SuppressWarnings("unused") public static void widen(SkinnyMethodAdapter mv, Class from, Class to, NativeType nativeType) { if (isPrimitiveInt(from)) { if (nativeType == NativeType.UCHAR) { @@ -144,23 +152,36 @@ public static void widen(SkinnyMethodAdapter mv, Class from, Class to, NativeTyp public static void narrow(SkinnyMethodAdapter mv, Class from, Class to) { if (!from.equals(to)) { if (byte.class == to || short.class == to || char.class == to || int.class == to || boolean.class == to) { - if (long.class == from) { - mv.l2i(); - } - - if (byte.class == to) { - mv.i2b(); + if (boolean.class == to) { + if (long.class == from) { + mv.lconst_0(); + mv.lcmp(); + } + /* Equivalent to + return result == 0 ? true : false; + */ + Label zero = new Label(); + Label ret = new Label(); + mv.ifeq(zero); + mv.iconst_1(); + mv.go_to(ret); + mv.label(zero); + mv.iconst_0(); + mv.label(ret); + } else { + if (long.class == from) { + mv.l2i(); + } - } else if (short.class == to) { - mv.i2s(); + if (byte.class == to) { + mv.i2b(); - } else if (char.class == to) { - mv.i2c(); + } else if (short.class == to) { + mv.i2s(); - } else if (boolean.class == to) { - // Ensure only 0x0 and 0x1 values are used for boolean - mv.iconst_1(); - mv.iand(); + } else if (char.class == to) { + mv.i2c(); + } } } } @@ -175,23 +196,67 @@ public static void convertPrimitive(SkinnyMethodAdapter mv, final Class from, fi public static void convertPrimitive(SkinnyMethodAdapter mv, final Class from, final Class to, final NativeType nativeType) { if (boolean.class == to) { - narrow(mv, from, to); + switch (nativeType) { + case SCHAR: + case UCHAR: + case SSHORT: + case USHORT: + case SINT: + case UINT: + case SLONG: + case ULONG: + case ADDRESS: + if (sizeof(nativeType) <= 4) { + narrow(mv, from, int.class); + switch (nativeType) { + // some compiler may not clean higher bits + // https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention + // Parameters less than 64 bits long are not zero extended; the high bits are not zeroed. + // such as we can still get 0x80000000 from `u_int32_t2u_int8_t(0x80000000)` + case SCHAR: + case UCHAR: + narrow(mv, int.class, byte.class); + break; + case USHORT: + case SSHORT: + narrow(mv, int.class, short.class); + break; + } + narrow(mv, int.class, to); + } else { + narrow(mv, from, to); + } + break; + case FLOAT: + case DOUBLE: + // TODO + break; + default: + narrow(mv, from, to); + break; + } return; } switch (nativeType) { case SCHAR: narrow(mv, from, byte.class); + // maybe to is char.class + narrow(mv, byte.class, to); widen(mv, byte.class, to); break; case SSHORT: narrow(mv, from, short.class); + // `to` may be byte.class + narrow(mv, short.class, to); widen(mv, short.class, to); break; case SINT: narrow(mv, from, int.class); + // `to` may be byte.class + narrow(mv, int.class, to); widen(mv, int.class, to); break; @@ -199,6 +264,8 @@ public static void convertPrimitive(SkinnyMethodAdapter mv, final Class from, fi narrow(mv, from, int.class); mv.pushInt(0xff); mv.iand(); + // `to` may be byte.class + narrow(mv, int.class, to); widen(mv, int.class, to); break; @@ -206,6 +273,8 @@ public static void convertPrimitive(SkinnyMethodAdapter mv, final Class from, fi narrow(mv, from, int.class); mv.pushInt(0xffff); mv.iand(); + // `to` may be byte.class + narrow(mv, int.class, to); widen(mv, int.class, to); break; @@ -219,8 +288,13 @@ public static void convertPrimitive(SkinnyMethodAdapter mv, final Class from, fi // strip off bits 32:63 mv.ldc(0xffffffffL); mv.land(); + } else { + // `to` may be byte.class + narrow(mv, int.class, to); } } else { + // `to` may be byte.class + narrow(mv, from, to); widen(mv, from, to); } break; diff --git a/src/main/java/jnr/ffi/provider/jffi/Types.java b/src/main/java/jnr/ffi/provider/jffi/Types.java index 6fd9a0e69..0f00c6ce3 100755 --- a/src/main/java/jnr/ffi/provider/jffi/Types.java +++ b/src/main/java/jnr/ffi/provider/jffi/Types.java @@ -92,6 +92,9 @@ static Type lookupType(jnr.ffi.Runtime runtime, Class type, Collection 4) { - if (parameterTypes[i].getNativeType() == NativeType.SLONGLONG && long.class != parameterClasses[i]) { - // sign extend from int.class -> long long - a.sar(eax, imm(31)); - - } else if (parameterTypes[i].getNativeType() == NativeType.ULONGLONG && long.class != parameterClasses[i]) { - // zero extend from int.class -> unsigned long long - a.mov(dword_ptr(esp, dstoff + 4), imm(0)); - - } else { - a.mov(eax, dword_ptr(esp, disp + 4)); + switch (parameterTypes[i].getNativeType()) { + case SLONGLONG: + case ULONGLONG: + if (long.class != parameterClasses[i]) { + // sign extend from int.class -> long long + a.sar(eax, imm(31)); + } else { + a.mov(eax, dword_ptr(esp, disp + 4)); + } + break; + case DOUBLE: + a.mov(eax, dword_ptr(esp, disp + 4)); + break; + default: + // impossible + break; } a.mov(dword_ptr(esp, dstoff + 4), eax); } @@ -283,6 +289,18 @@ void compile(Function function, String name, ResultType resultType, ParameterTyp } } + if (boolean.class == resultClass) { + switch (resultType.getNativeType()) { + case SLONGLONG: + case ULONGLONG: + a.or_(eax, edx); + break; + default: + a.test(eax, eax); + break; + } + a.setne(al); + } // Restore esp to the original position and return a.add(esp, imm(stackadj)); a.ret(); @@ -315,7 +333,7 @@ static int parameterSize(ParameterType parameterType) { } static int parameterSize(Class t) { - if (byte.class == t || short.class == t || char.class == t | int.class == t || float.class == t) { + if (boolean.class == t || byte.class == t || short.class == t || char.class == t | int.class == t || float.class == t) { return 4; } else if (long.class == t || double.class == t) { diff --git a/src/main/java/jnr/ffi/provider/jffi/X86_64StubCompiler.java b/src/main/java/jnr/ffi/provider/jffi/X86_64StubCompiler.java index 28bd13764..ee3c0a724 100644 --- a/src/main/java/jnr/ffi/provider/jffi/X86_64StubCompiler.java +++ b/src/main/java/jnr/ffi/provider/jffi/X86_64StubCompiler.java @@ -178,6 +178,15 @@ final void compile(Function function, String name, ResultType resultType, Parame a.mov(dstRegisters32[i], srcRegisters32[i]); break; + case SLONGLONG: + case ULONGLONG: + if (parameterTypes[i].getDeclaredType() != long.class) { + a.movsxd(dstRegisters64[i], srcRegisters32[i]); + } else { + a.mov(dstRegisters64[i], srcRegisters64[i]); + } + break; + default: a.mov(dstRegisters64[i], srcRegisters64[i]); break; @@ -217,6 +226,15 @@ final void compile(Function function, String name, ResultType resultType, Parame a.mov(dstRegisters32[i], dword_ptr(rsp, disp)); break; + case SLONGLONG: + case ULONGLONG: + if (parameterTypes[i].getDeclaredType() != long.class) { + a.movsxd(dstRegisters64[i], dword_ptr(rsp, disp)); + } else { + a.mov(dstRegisters64[i], qword_ptr(rsp, disp)); + } + break; + default: a.mov(dstRegisters64[i], qword_ptr(rsp, disp)); break; @@ -343,6 +361,11 @@ final void compile(Function function, String name, ResultType resultType, Parame } } + if (boolean.class == resultType.getDeclaredType()) { + a.test(rax, rax); + a.setne(rax); + } + // Restore rsp to original position a.add(rsp, imm(space)); a.ret();