Package org.lwjgl.system.ffm
The objective is to allow users to create their own LWJGL-style bindings, using a fluent API, dynamically at run-time, without sacrificing performance or worrying about best practices and low-level details.
Several modern Java technologies are being utilized to achieve this:
- Dynamic Class-File Constants (JEP 309, JDK 11, a.k.a. Condy): Used to lazily initialize bindings, without precompiling a private nested class per function.
- Hidden Classes (JEP 371, JDK 15): Used to generate classes with minimal metadata and no interaction with ClassLoaders.
- Class data support for hidden classes (JDK-8256214, JDK 16): Used to boostrap Condy values.
- Foreign Function & Memory API (JEP 442, JDK 22): Used for native interoperation without JNI.
- Class-File API (JEP 457, JDK 24): Used to generate bytecode at runtime, without 3rd-party dependencies.
The FFM.ffmGenerate(Class) method is designed to be relatively cheap. The instance returned is lightweight and its methods are
never materialized, unless actually used by the application. More specifically:
- A minimal class is generated that implements the input interface.
- There is no state in the generated class, only methods.
- All methods are simple, with minimal bytecode. A method handle is retrieved using Condy and arguments are passed directly to
MethodHandle.invokeExact(Object...). - There is no overhead from the Condy
ldcinstruction. It is executed only once, if and when the method is called. -
Execution of relatively expensive binding code is deferred to the Condy bootstrap. This includes:
- lookup of
FFM*annotations and associated logic - function address lookup and creation of FFM method handles
- further bytecode generation and wrapping of FFM method handles if necessary
- lookup of
-
Hidden class data is used:
- to pass reflected
Methodinstances to the Condy bootstrap - to pass FFM
MethodHandle/MemorySegmentinstances to generated wrapper methods
- to pass reflected
When stored in static final fields, performance of the generated bindings is equivalent to direct FFM calls. The JVM can inline through
everything, for the following reasons:
- The binding instance is constant.
- There is a single implementation of the binding interface.
- The
MethodHandleinstances created via Condy are also treated as constants by the JVM.
Downcalls support the specification of 3 virtual parameters, which must be present in a strict order before other parameters:
-
A
MemorySegmentparameter in methods annotated withFFMFunctionAddress.It must be specified before any other virtual or normal parameter.
-
A
SegmentAllocatororStackAllocatorparameter. This parameter will be used to allocate storage for struct values passed or returned by-value and also for temporary storage needed internally by the method call (e.g. forFFMReturnbuffers).If the parameter is of type
StackAllocator, a stack frame will be pushed & popped inside the method call when temporary storage is needed.It must be specified after the
FFMFunctionAddressparameter, if one exists, and before any other virtual or normal parameter. -
A
MemorySegmentparameter annotated withFFMCaptureCallState.It must be specified after other virtual parameters and before any normal parameters.
Upcalls and structs/unions are also supported. They are also defined as interfaces (FunctionalInterface in the case of upcalls), but there are
two classes generated:
- The upcall or struct/union implementation itself.
- A binder implementation, which is used to allocate and access the corresponding type. The binder instance must be declared as a field in the interface and functions like a factory API for the type.
See UpcallBinder and GroupBinder for details.
-
ClassDescriptionThis class is the entry point for the Runtime Bindings Generator API.A builder class for
FFMConfig.FFM.GroupBinderBuilder<T, L extends GroupLayout, M extends GroupBinder<L,T>, SELF extends FFM.GroupBinderBuilder<T, L, M, SELF>> Base class for struct/union binder builders.A builder forStructBinderinstances.A builder forUnionBinderinstances.Maps a native integer type, that is larger than 1 byte, to a Java boolean.Marks a struct/union parameter/result that is passed/returned by value, rather than by reference.Marks a struct/union getter as the canonical getter for the corresponding member.Enables theLinker.Option.captureCallState(String...)option when creating the FFM downcall.Specifies the charset used for string values.Binding configuration, used to customize the generation of binding implementations.Enables theLinker.Option.critical(boolean)option when creating the FFM downcall.Defines a function signature or a struct/union.Marks the start of the variadic arguments for a variadic function.When specified, the native function address will be passed explicitly on every call as the first argument, which must be of typeMemorySegment.Adds two leading pointer parameters to which NULL will be passed implicitly.Overrides the native function name.If specified, theFFMPrefixis not applied to this method.Marks an unsafe pointer parameter as nullable.Marks alongparameter as a raw pointer.Adds a prefix to all native function names.Transforms a native function such that it returns the value of an output parameter.When specified, the parameter will be used as the size of the internally allocated return buffer.Specifies the output parameter that receives the actual buffer size.Marks a group member as auto-sized by another struct memberA struct or union interface may extend this capability interface to exposeGroupBinderfunctionality at the instance level.GroupArray<L extends GroupLayout, T>Can be used to create aMemorySegmentview as an array of structs or unions.GroupBinder<L extends GroupLayout, T>The base binder class for struct and union interfaces.An enumeration of carrier types for parameters that represent integer sizes.StackAllocator<T extends StackAllocator<T>>ASegmentAllocatorthat supports stack-like push/pop functionality.A struct interface may extend this capability interface to exposeStructBinderfunctionality at the instance level.StructArray<T>Can be used to create aMemorySegmentview as an array of structs.StructBinder<T>The binder class for struct interfaces.A consumer of traced method calls.A union interface may extend this capability interface to exposeUnionBinderfunctionality at the instance level.UnionArray<T>Can be used to create aMemorySegmentview as an array of unions.UnionBinder<T>The binder class for union interfaces.UpcallBinder<T>The binder class for upcall interfaces.