Java-GI is intended to run on 64-bit Windows, Linux and MacOS systems. However, sizeof(long) differs between these platforms, and needs to be accounted for. Consult the table in https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models for context. Currently java-gi assumes sizeof(long) == 64. This is incorrect on Windows.
Ideally this is resolved without creating platform-specific Java-GI jar files. The Java bindings can provide the "lowest common denominator", i.e. always use int in Java when the native type is long. When allocating memory and calling native functions, the marshaling code can use the actual data type (32 or 64 bit) at runtime.
Investigation
It's probably useful to create a small tool that logs all occurences of long in the gir files so it will be possible to investigate the specific examples and how often they occur. If a specific situation is very uncommon, it may be best to simply exclude it from the Java bindings.
Runtime detection
We can detect at runtime what platform we're on, by adding a utility method Interop::longAsInt that returns true when sizeof(long) is 32 bit (i.e. on Windows). It is determined using:
The result can be stored in a static field for performance reasons.
Memory layouts
Generating memory layouts
For memory layouts with long fields, generate two layouts, one with 64-bit long fields (ValueLayout.JAVA_LONG) and one with 32-bit (ValueLayout.JAVA_INT). This can result in different padding. Calling getMemoryLayout() will return one of the two layouts.
Field accessors and Record allocators
Field read() and write() methods for long fields must always use int on all platforms.
Because the VarHandles use normal invoke behavior (as opposed to invokeExact behavior), the int values are widened automatically to long when appropriate (the VarHandle is created from the generated memory layout).
This prevents reading and writing actual long values (larger than Integer.MAX_VALUE) in Linux/MacOS, even though the C API allows it there, to preserve platform-independent behavior.
The Record constructors must override long parameters with int, but do not require any other changes.
Methods, Callbacks and Signals
Method invocation and parameter marshaling
When a method has long parameters or return value, generate two MethodHandle invocations, one for LLP64 (Windows) long values and one with LP64 (Linux/MacOS).
The LLP64 version can work as usual, assuming 32 bit sizes (int values) in Java and in C.
In the second version, long parameters and return values should be int in Java, but need a cast to long in the MethodHandle invocation.
long parameters must insert a (long) cast in the parameter list
long return values must prepend a (int) (long) cast (currently it is only a (long) cast).
Function descriptor
When generating a function descriptor with long parameters, generate those layouts with a ternary expression Interop.longAsInt() ? ValueLayout.LONG : ValueLayout.INT.
Aliases
Aliases (typedefs) for long do not seem to exist currently (in GNOME 46 gir files).
Java-GI is intended to run on 64-bit Windows, Linux and MacOS systems. However,
sizeof(long)
differs between these platforms, and needs to be accounted for. Consult the table in https://en.wikipedia.org/wiki/64-bit_computing#64-bit_data_models for context. Currently java-gi assumessizeof(long) == 64
. This is incorrect on Windows.Ideally this is resolved without creating platform-specific Java-GI jar files. The Java bindings can provide the "lowest common denominator", i.e. always use
int
in Java when the native type islong
. When allocating memory and calling native functions, the marshaling code can use the actual data type (32 or 64 bit) at runtime.Investigation
It's probably useful to create a small tool that logs all occurences of
long
in the gir files so it will be possible to investigate the specific examples and how often they occur. If a specific situation is very uncommon, it may be best to simply exclude it from the Java bindings.Runtime detection
We can detect at runtime what platform we're on, by adding a utility method
Interop::longAsInt
that returnstrue
whensizeof(long)
is 32 bit (i.e. on Windows). It is determined using:<linker>.canonicalLayouts().get("long").equals(ValueLayout.JAVA_LONG)
The result can be stored in a static field for performance reasons.
Memory layouts
Generating memory layouts
For memory layouts with
long
fields, generate two layouts, one with 64-bitlong
fields (ValueLayout.JAVA_LONG
) and one with 32-bit (ValueLayout.JAVA_INT
). This can result in different padding. CallinggetMemoryLayout()
will return one of the two layouts.Field accessors and Record allocators
Field
read()
andwrite()
methods forlong
fields must always useint
on all platforms.invoke
behavior (as opposed toinvokeExact
behavior), theint
values are widened automatically tolong
when appropriate (the VarHandle is created from the generated memory layout).Integer.MAX_VALUE
) in Linux/MacOS, even though the C API allows it there, to preserve platform-independent behavior.The Record constructors must override
long
parameters withint
, but do not require any other changes.Methods, Callbacks and Signals
Method invocation and parameter marshaling
When a method has
long
parameters or return value, generate two MethodHandle invocations, one for LLP64 (Windows)long
values and one with LP64 (Linux/MacOS).The LLP64 version can work as usual, assuming 32 bit sizes (
int
values) in Java and in C.In the second version,
long
parameters and return values should beint
in Java, but need a cast tolong
in the MethodHandle invocation.long
parameters must insert a(long)
cast in the parameter listlong
return values must prepend a(int) (long)
cast (currently it is only a(long)
cast).Function descriptor
When generating a function descriptor with
long
parameters, generate those layouts with a ternary expressionInterop.longAsInt() ? ValueLayout.LONG : ValueLayout.INT
.Aliases
Aliases (
typedef
s) forlong
do not seem to exist currently (in GNOME 46 gir files).