jwharm / java-gi

GObject-Introspection bindings generator for Java
GNU Lesser General Public License v2.1
93 stars 9 forks source link

Windows support (32-bit long values) #106

Closed jwharm closed 4 months ago

jwharm commented 5 months ago

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:

<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-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.

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.

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).