Add jextract module: SQLite bindings generated with jextract, FFM arenas, C strings, out-parameters, errors, upcalls, and a JMH comparison with JNI
jextract-25 early-access build (25-jextract+2-4) on JDK 25.0.5+8 against SQLite 3.45.1: 25 functions and 13 constants generated into 4 committed files (the whole header would be 83 files), a small Db wrapper, the four arena kinds used wrongly on purpose, ADDRESS vs C_POINTER zero-length vs unbounded segments and the SIGSEGV from getString on a NULL pointer, sqlite3_open out-parameter, return codes to exceptions, a sqlite3_exec upcall and the VM exit when it throws, the JDK 25 native-access warning and --illegal-native-access=deny, library loading failures, and a JMH benchmark of FFM vs hand-written JNI vs plain Java (indicative only: shared 2-CPU machine). Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b6Kg9RWSTJzyz6KP9X
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
// Generated by jextract
|
||||
|
||||
package demo.sqlite;
|
||||
|
||||
import java.lang.invoke.*;
|
||||
import java.lang.foreign.*;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.*;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.*;
|
||||
import static java.lang.foreign.MemoryLayout.PathElement.*;
|
||||
|
||||
/**
|
||||
* {@snippet lang=c :
|
||||
* void (*)(void *)
|
||||
* }
|
||||
*/
|
||||
public final class sqlite3_bind_text$x0 {
|
||||
|
||||
private sqlite3_bind_text$x0() {
|
||||
// Should not be called directly
|
||||
}
|
||||
|
||||
/**
|
||||
* The function pointer signature, expressed as a functional interface
|
||||
*/
|
||||
public interface Function {
|
||||
void apply(MemorySegment _x0);
|
||||
}
|
||||
|
||||
private static final FunctionDescriptor $DESC = FunctionDescriptor.ofVoid(
|
||||
sqlite3_h.C_POINTER
|
||||
);
|
||||
|
||||
/**
|
||||
* The descriptor of this function pointer
|
||||
*/
|
||||
public static FunctionDescriptor descriptor() {
|
||||
return $DESC;
|
||||
}
|
||||
|
||||
private static final MethodHandle UP$MH = sqlite3_h.upcallHandle(sqlite3_bind_text$x0.Function.class, "apply", $DESC);
|
||||
|
||||
/**
|
||||
* Allocates a new upcall stub, whose implementation is defined by {@code fi}.
|
||||
* The lifetime of the returned segment is managed by {@code arena}
|
||||
*/
|
||||
public static MemorySegment allocate(sqlite3_bind_text$x0.Function fi, Arena arena) {
|
||||
return Linker.nativeLinker().upcallStub(UP$MH.bindTo(fi), $DESC, arena);
|
||||
}
|
||||
|
||||
private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC);
|
||||
|
||||
/**
|
||||
* Invoke the upcall stub {@code funcPtr}, with given parameters
|
||||
*/
|
||||
public static void invoke(MemorySegment funcPtr, MemorySegment _x0) {
|
||||
try {
|
||||
DOWN$MH.invokeExact(funcPtr, _x0);
|
||||
} catch (Error | RuntimeException ex) {
|
||||
throw ex;
|
||||
} catch (Throwable ex$) {
|
||||
throw new AssertionError("should not reach here", ex$);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
// Generated by jextract
|
||||
|
||||
package demo.sqlite;
|
||||
|
||||
import java.lang.invoke.*;
|
||||
import java.lang.foreign.*;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.*;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.*;
|
||||
import static java.lang.foreign.MemoryLayout.PathElement.*;
|
||||
|
||||
/**
|
||||
* {@snippet lang=c :
|
||||
* int (*callback)(void *, int, char **, char **)
|
||||
* }
|
||||
*/
|
||||
public final class sqlite3_exec$callback {
|
||||
|
||||
private sqlite3_exec$callback() {
|
||||
// Should not be called directly
|
||||
}
|
||||
|
||||
/**
|
||||
* The function pointer signature, expressed as a functional interface
|
||||
*/
|
||||
public interface Function {
|
||||
int apply(MemorySegment _x0, int _x1, MemorySegment _x2, MemorySegment _x3);
|
||||
}
|
||||
|
||||
private static final FunctionDescriptor $DESC = FunctionDescriptor.of(
|
||||
sqlite3_h.C_INT,
|
||||
sqlite3_h.C_POINTER,
|
||||
sqlite3_h.C_INT,
|
||||
sqlite3_h.C_POINTER,
|
||||
sqlite3_h.C_POINTER
|
||||
);
|
||||
|
||||
/**
|
||||
* The descriptor of this function pointer
|
||||
*/
|
||||
public static FunctionDescriptor descriptor() {
|
||||
return $DESC;
|
||||
}
|
||||
|
||||
private static final MethodHandle UP$MH = sqlite3_h.upcallHandle(sqlite3_exec$callback.Function.class, "apply", $DESC);
|
||||
|
||||
/**
|
||||
* Allocates a new upcall stub, whose implementation is defined by {@code fi}.
|
||||
* The lifetime of the returned segment is managed by {@code arena}
|
||||
*/
|
||||
public static MemorySegment allocate(sqlite3_exec$callback.Function fi, Arena arena) {
|
||||
return Linker.nativeLinker().upcallStub(UP$MH.bindTo(fi), $DESC, arena);
|
||||
}
|
||||
|
||||
private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC);
|
||||
|
||||
/**
|
||||
* Invoke the upcall stub {@code funcPtr}, with given parameters
|
||||
*/
|
||||
public static int invoke(MemorySegment funcPtr, MemorySegment _x0, int _x1, MemorySegment _x2, MemorySegment _x3) {
|
||||
try {
|
||||
return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3);
|
||||
} catch (Error | RuntimeException ex) {
|
||||
throw ex;
|
||||
} catch (Throwable ex$) {
|
||||
throw new AssertionError("should not reach here", ex$);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// Generated by jextract
|
||||
|
||||
package demo.sqlite;
|
||||
|
||||
import java.lang.invoke.*;
|
||||
import java.lang.foreign.*;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
import java.util.stream.*;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.*;
|
||||
import static java.lang.foreign.MemoryLayout.PathElement.*;
|
||||
|
||||
public class sqlite3_h$shared {
|
||||
|
||||
sqlite3_h$shared() {
|
||||
// Should not be called directly
|
||||
}
|
||||
|
||||
public static final ValueLayout.OfBoolean C_BOOL = (ValueLayout.OfBoolean) Linker.nativeLinker().canonicalLayouts().get("bool");
|
||||
public static final ValueLayout.OfByte C_CHAR =(ValueLayout.OfByte)Linker.nativeLinker().canonicalLayouts().get("char");
|
||||
public static final ValueLayout.OfShort C_SHORT = (ValueLayout.OfShort) Linker.nativeLinker().canonicalLayouts().get("short");
|
||||
public static final ValueLayout.OfInt C_INT = (ValueLayout.OfInt) Linker.nativeLinker().canonicalLayouts().get("int");
|
||||
public static final ValueLayout.OfLong C_LONG_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long long");
|
||||
public static final ValueLayout.OfFloat C_FLOAT = (ValueLayout.OfFloat) Linker.nativeLinker().canonicalLayouts().get("float");
|
||||
public static final ValueLayout.OfDouble C_DOUBLE = (ValueLayout.OfDouble) Linker.nativeLinker().canonicalLayouts().get("double");
|
||||
public static final AddressLayout C_POINTER = ((AddressLayout) Linker.nativeLinker().canonicalLayouts().get("void*"))
|
||||
.withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, C_CHAR));
|
||||
public static final ValueLayout.OfLong C_LONG = (ValueLayout.OfLong) Linker.nativeLinker().canonicalLayouts().get("long");
|
||||
|
||||
static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls");
|
||||
|
||||
static void traceDowncall(String name, Object... args) {
|
||||
String traceArgs = Arrays.stream(args)
|
||||
.map(Object::toString)
|
||||
.collect(Collectors.joining(", "));
|
||||
System.out.printf("%s(%s)\n", name, traceArgs);
|
||||
}
|
||||
|
||||
static MethodHandle upcallHandle(Class<?> fi, String name, FunctionDescriptor fdesc) {
|
||||
try {
|
||||
return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType());
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new AssertionError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
static MemoryLayout align(MemoryLayout layout, long align) {
|
||||
return switch (layout) {
|
||||
case PaddingLayout p -> p;
|
||||
case ValueLayout v -> v.withByteAlignment(align);
|
||||
case GroupLayout g -> {
|
||||
MemoryLayout[] alignedMembers = g.memberLayouts().stream()
|
||||
.map(m -> align(m, align)).toArray(MemoryLayout[]::new);
|
||||
yield g instanceof StructLayout ?
|
||||
MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers);
|
||||
}
|
||||
case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user