final class Native {
static {
String preferredLibrary = "{{ module.lib_name }}_jni";
String fallbackLibrary = "{{ module.lib_name }}";
{%- if module.desktop_loader %}
String vmName = System.getProperty("java.vm.name");
if (vmName == null) vmName = "";
boolean isAndroidRuntime =
vmName.toLowerCase().contains("dalvik") ||
vmName.toLowerCase().contains("art");
if (isAndroidRuntime) {
System.loadLibrary(fallbackLibrary);
} else {
loadDesktopLibraries(preferredLibrary, fallbackLibrary);
}
{%- else %}
System.loadLibrary(fallbackLibrary);
{%- endif %}
}
private Native() {}
{%- if module.desktop_loader %}
private static volatile java.io.File bundledLibraryDirectory;
private static void loadDesktopLibraries(String preferredLibrary, String fallbackLibrary) {
UnsatisfiedLinkError preferredFailure = tryLoadDesktopLibrary(preferredLibrary);
if (preferredFailure == null) {
return;
}
if (tryLoadOptionalDesktopLibrary(fallbackLibrary)) {
preferredFailure = tryLoadDesktopLibrary(preferredLibrary);
if (preferredFailure == null) {
return;
}
}
throw preferredFailure;
}
private static UnsatisfiedLinkError tryLoadDesktopLibrary(String libraryName) {
try {
if (loadBundledLibraryIfPresent(libraryName) || loadExternalLibraryIfPresent(libraryName)) {
return null;
}
return new UnsatisfiedLinkError("Could not load native library '" + libraryName + "'");
} catch (UnsatisfiedLinkError error) {
return error;
}
}
private static boolean tryLoadOptionalDesktopLibrary(String libraryName) {
try {
return loadBundledLibraryIfPresent(libraryName) || loadExternalLibraryIfPresent(libraryName);
} catch (UnsatisfiedLinkError ignored) {
return false;
}
}
private static boolean loadExternalLibraryIfPresent(String libraryName) {
try {
System.loadLibrary(libraryName);
return true;
} catch (UnsatisfiedLinkError ignored) {
return false;
}
}
private static boolean loadBundledLibraryIfPresent(String libraryName) {
String mappedName = System.mapLibraryName(libraryName);
for (String resourcePath : bundledLibraryResourceCandidates(mappedName)) {
try (java.io.InputStream input = Native.class.getResourceAsStream(resourcePath)) {
if (input == null) continue;
java.io.File extracted = extractBundledLibrary(resourcePath, input);
System.load(extracted.getAbsolutePath());
return true;
} catch (java.io.IOException error) {
throw new ExceptionInInitializerError(error);
}
}
return false;
}
private static java.io.File extractBundledLibrary(
String resourcePath,
java.io.InputStream input
) throws java.io.IOException {
String fileName = resourcePath.substring(resourcePath.lastIndexOf('/') + 1);
java.io.File extracted = new java.io.File(bundledLibraryDirectory(), fileName);
if (!extracted.isFile()) {
copyBundledLibrary(input, extracted);
extracted.deleteOnExit();
}
return extracted;
}
private static void copyBundledLibrary(
java.io.InputStream input,
java.io.File extracted
) throws java.io.IOException {
try (java.io.OutputStream output = new java.io.FileOutputStream(extracted)) {
byte[] buffer = new byte[8192];
int read;
while ((read = input.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
}
}
private static java.io.File bundledLibraryDirectory() throws java.io.IOException {
java.io.File existing = bundledLibraryDirectory;
if (existing != null) {
return existing;
}
synchronized (Native.class) {
if (bundledLibraryDirectory == null) {
bundledLibraryDirectory = createBundledLibraryDirectory();
}
return bundledLibraryDirectory;
}
}
private static java.io.File createBundledLibraryDirectory() throws java.io.IOException {
java.io.File created = java.io.File.createTempFile("boltffi-native-", "");
if (!created.delete() || !created.mkdir()) {
throw new java.io.IOException("failed to create temp directory for bundled native extraction");
}
created.deleteOnExit();
return created;
}
private static java.util.List<String> bundledLibraryResourceCandidates(String mappedName) {
java.util.ArrayList<String> candidates = new java.util.ArrayList<>();
for (String directory : desktopNativeDirectories()) {
candidates.add("/" + directory + "/" + mappedName);
candidates.add("/native/" + directory + "/" + mappedName);
}
candidates.add("/" + mappedName);
return candidates;
}
private static java.util.List<String> desktopNativeDirectories() {
String osName = System.getProperty("os.name", "").toLowerCase();
String osArch = System.getProperty("os.arch", "").toLowerCase();
if ((osName.contains("mac") || osName.contains("darwin"))
&& (osArch.equals("aarch64") || osArch.equals("arm64"))) {
return java.util.Arrays.asList("darwin-arm64", "darwin-aarch64");
}
if ((osName.contains("mac") || osName.contains("darwin")) && osArch.equals("x86_64")) {
return java.util.Arrays.asList("darwin-x86_64", "darwin-x86-64");
}
if (osName.contains("linux") && (osArch.equals("x86_64") || osArch.equals("amd64"))) {
return java.util.Arrays.asList("linux-x86_64", "linux-x86-64");
}
if (osName.contains("linux") && (osArch.equals("aarch64") || osArch.equals("arm64"))) {
return java.util.Arrays.asList("linux-aarch64", "linux-arm64");
}
if (osName.contains("windows") && (osArch.equals("x86_64") || osArch.equals("amd64"))) {
return java.util.Arrays.asList("windows-x86_64", "windows-x86-64", "win32-x86_64");
}
return java.util.Collections.emptyList();
}
{%- endif %}
@SuppressWarnings("unused")
static void boltffiFutureContinuationCallback(long handle, byte pollResult) {
{%- if module.has_async() || module.has_streams() %}
BoltFFIContinuationMap.complete(handle, pollResult);
{%- endif %}
}
static native void {{ module.prefix }}_free_string(long ptr);
static native byte[] {{ module.prefix }}_last_error_message();
{%- for func in module.functions %}
{%- if func.is_async() %}
{%- match func.async_call %}
{%- when Some with (ac) %}
static native long {{ func.ffi_name }}({% for param in func.params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
static native void {{ ac.poll }}(long future, long contHandle);
static native {{ func.native_return_type() }} {{ ac.complete }}(long future);
static native void {{ ac.cancel }}(long future);
static native void {{ ac.free }}(long future);
{%- when None %}
{%- endmatch %}
{%- else %}
static native {{ func.native_return_type() }} {{ func.ffi_name }}({% for param in func.params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- endif %}
{%- endfor %}
{%- for record in module.records %}
{%- for ctor in record.constructors %}
static native {{ ctor.return_plan.native_return_type }} {{ ctor.ffi_name }}({% for param in ctor.native_params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- endfor %}
{%- for method in record.methods %}
static native {{ method.return_plan.native_return_type }} {{ method.ffi_name }}({% for param in method.native_params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- endfor %}
{%- endfor %}
{%- for enumeration in module.enums %}
{%- for ctor in enumeration.constructors %}
static native {{ ctor.return_plan.native_return_type }} {{ ctor.ffi_name }}({% for param in ctor.native_params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- endfor %}
{%- for method in enumeration.methods %}
static native {{ method.return_plan.native_return_type }} {{ method.ffi_name }}({% for param in method.native_params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- endfor %}
{%- endfor %}
{%- for class in module.classes %}
static native void {{ class.ffi_free }}(long handle);
{%- for ctor in class.constructors %}
static native long {{ ctor.ffi_name }}({% for param in ctor.params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- endfor %}
{%- for method in class.methods %}
{%- if method.is_async() %}
{%- match method.async_call %}
{%- when Some with (ac) %}
{%- if method.is_static %}
static native long {{ method.ffi_name }}({% for param in method.params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
static native void {{ ac.poll }}(long future, long contHandle);
static native {{ method.native_return_type() }} {{ ac.complete }}(long future);
static native void {{ ac.cancel }}(long future);
static native void {{ ac.free }}(long future);
{%- else %}
static native long {{ method.ffi_name }}(long handle{% if !method.params.is_empty() %}, {% endif %}{% for param in method.params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
static native void {{ ac.poll }}(long handle, long future, long contHandle);
static native {{ method.native_return_type() }} {{ ac.complete }}(long handle, long future);
static native void {{ ac.cancel }}(long handle, long future);
static native void {{ ac.free }}(long handle, long future);
{%- endif %}
{%- when None %}
{%- endmatch %}
{%- else %}
{%- if method.is_static %}
static native {{ method.native_return_type() }} {{ method.ffi_name }}({% for param in method.params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- else %}
static native {{ method.native_return_type() }} {{ method.ffi_name }}(long handle{% if !method.params.is_empty() %}, {% endif %}{% for param in method.params %}{{ param.native_type }} {{ param.name }}{% if !loop.last %}, {% endif %}{% endfor %});
{%- endif %}
{%- endif %}
{%- endfor %}
{%- for stream in class.streams %}
static native long {{ stream.subscribe }}(long handle);
static native void {{ stream.poll }}(long subscription, long contHandle);
static native byte[] {{ stream.pop_batch }}(long subscription, long maxCount);
static native int {{ stream.wait }}(long subscription, int timeout);
static native void {{ stream.unsubscribe }}(long subscription);
static native void {{ stream.free }}(long subscription);
{%- endfor %}
{%- endfor %}
{%- for invoker in module.async_callback_invokers %}
static native void {{ invoker.success_name() }}(long callbackPtr, long callbackData{% if invoker.has_result() %}, {{ invoker.result_jni_type() }} result{% endif %});
static native void {{ invoker.failure_name() }}(long callbackPtr, long callbackData);
{%- endfor %}
{%- for closure in module.closures %}
{%- if closure.supports_proxy_wrap %}
static native long {{ closure.proxy_clone_native_name() }}(long handle);
static native void {{ closure.proxy_release_native_name() }}(long handle);
static native {{ closure.proxy.return_plan.native_return_type }} {{ closure.proxy.native_name }}(long handle{% for param in closure.proxy.params %}, {{ param.native_type }} {{ param.name }}{% endfor %});
{%- endif %}
{%- endfor %}
{%- for callback in module.callbacks %}
static native long {{ callback.proxy_clone_native_name() }}(long handle);
static native void {{ callback.proxy_release_native_name() }}(long handle);
{%- for method in callback.sync_methods %}
static native {{ method.proxy.return_plan.native_return_type }} {{ method.proxy.native_name }}(long handle{% for param in method.proxy.params %}, {{ param.native_type }} {{ param.name }}{% endfor %});
{%- endfor %}
{%- for method in callback.async_methods %}
static native void {{ method.proxy.native_name }}(long handle{% for param in method.proxy.params %}, {{ param.native_type }} {{ param.name }}{% endfor %}, long callbackData);
{%- endfor %}
{%- endfor %}
}