Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix number conversions #153

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/main/java/jnr/ffi/Struct.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/jnr/ffi/StructLayout.java
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/jnr/ffi/byref/NumberByReference.java
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/jnr/ffi/provider/jffi/AsmLibraryLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
11 changes: 10 additions & 1 deletion src/main/java/jnr/ffi/provider/jffi/AsmUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {
Expand Down
27 changes: 25 additions & 2 deletions src/main/java/jnr/ffi/provider/jffi/DefaultInvokerFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -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())));
Expand Down Expand Up @@ -241,6 +244,9 @@ static Marshaller getMarshaller(Class type, NativeType nativeType, Collection<An
} else if (Boolean.class.isAssignableFrom(type) || boolean.class == type) {
return BooleanMarshaller.INSTANCE;

} else if (Character.class.isAssignableFrom(type) || char.class == type) {
return CharacterMarshaller.INSTANCE;

} else if (Pointer.class.isAssignableFrom(type)) {
return new PointerMarshaller(annotations);

Expand Down Expand Up @@ -506,10 +512,20 @@ public final Object invoke(Runtime runtime, Function function, HeapInvocationBuf
static class BooleanInvoker extends BaseInvoker {
static FunctionInvoker INSTANCE = new BooleanInvoker();
public final Object invoke(Runtime runtime, Function function, HeapInvocationBuffer buffer) {
if (function.getReturnType().size() == 8) {
return invoker.invokeLong(function, buffer) != 0;
}
return invoker.invokeInt(function, buffer) != 0;
}
}

static class CharacterInvoker extends BaseInvoker {
static FunctionInvoker INSTANCE = new CharacterInvoker();
public final Object invoke(Runtime runtime, Function function, HeapInvocationBuffer buffer) {
return (char) invoker.invokeInt(function, buffer);
}
}

static class IntInvoker extends BaseInvoker {
static final FunctionInvoker INSTANCE = new IntInvoker();
public final Object invoke(Runtime runtime, Function function, HeapInvocationBuffer buffer) {
Expand Down Expand Up @@ -552,6 +568,13 @@ public void marshal(InvocationSession session, HeapInvocationBuffer buffer, Obje
}
}

static class CharacterMarshaller implements Marshaller {
static final CharacterMarshaller INSTANCE = new CharacterMarshaller();
public void marshal(InvocationSession session, HeapInvocationBuffer buffer, Object parameter) {
buffer.putInt(((Character) parameter).charValue());
}
}

static class Int8Marshaller implements Marshaller {
private final ToNativeConverter<Number, Number> toNativeConverter;

Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -868,7 +891,7 @@ static final class BooleanConverter implements DataConverter<Boolean, Number> {
static final DataConverter<Boolean, Number> INSTANCE = new BooleanConverter();
@Override
public Boolean fromNative(Number nativeValue, FromNativeContext context) {
return (nativeValue.intValue() & 0x1) != 0;
return nativeValue.longValue() != 0;
}

@Override
Expand Down
106 changes: 90 additions & 16 deletions src/main/java/jnr/ffi/provider/jffi/NumberUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import jnr.ffi.NativeType;
import jnr.ffi.provider.SigType;
import org.objectweb.asm.Label;

public final class NumberUtil {
private NumberUtil() {}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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();
}
}
}
}
Expand All @@ -175,37 +196,85 @@ 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;

case UCHAR:
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;

case USHORT:
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;

Expand All @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/jnr/ffi/provider/jffi/Types.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ static Type lookupType(jnr.ffi.Runtime runtime, Class type, Collection<Annotatio
} else if (Short.class.isAssignableFrom(type) || short.class == type) {
return runtime.findType(NativeType.SSHORT);

} else if (Character.class.isAssignableFrom(type) || char.class == type) {
return runtime.findType(NativeType.USHORT);

} else if (Integer.class.isAssignableFrom(type) || int.class == type) {
return runtime.findType(NativeType.SINT);

Expand Down
Loading