+c_file_pfx = """#include \"org_ldk_impl_bindings.h\"
+#include <rust_types.h>
+#include <lightning.h>
+#include <string.h>
+#include <stdatomic.h>
+"""
+
+if sys.argv[5] == "false":
+ c_file_pfx = c_file_pfx + """#define MALLOC(a, _) malloc(a)
+#define FREE(p) if ((long)(p) > 1024) { free(p); }
+#define DO_ASSERT(a) (void)(a)
+#define CHECK(a)
+"""
+else:
+ c_file_pfx = c_file_pfx + """#include <assert.h>
+// Always run a, then assert it is true:
+#define DO_ASSERT(a) do { bool _assert_val = (a); assert(_assert_val); } while(0)
+// Assert a is true or do nothing
+#define CHECK(a) DO_ASSERT(a)
+
+// Running a leak check across all the allocations and frees of the JDK is a mess,
+// so instead we implement our own naive leak checker here, relying on the -wrap
+// linker option to wrap malloc/calloc/realloc/free, tracking everyhing allocated
+// and free'd in Rust or C across the generated bindings shared library.
+#include <threads.h>
+#include <execinfo.h>
+#include <unistd.h>
+static mtx_t allocation_mtx;
+
+void __attribute__((constructor)) init_mtx() {
+ DO_ASSERT(mtx_init(&allocation_mtx, mtx_plain) == thrd_success);
+}
+
+#define BT_MAX 128
+typedef struct allocation {
+ struct allocation* next;
+ void* ptr;
+ const char* struct_name;
+ void* bt[BT_MAX];
+ int bt_len;
+} allocation;
+static allocation* allocation_ll = NULL;
+
+void* __real_malloc(size_t len);
+void* __real_calloc(size_t nmemb, size_t len);
+static void new_allocation(void* res, const char* struct_name) {
+ allocation* new_alloc = __real_malloc(sizeof(allocation));
+ new_alloc->ptr = res;
+ new_alloc->struct_name = struct_name;
+ new_alloc->bt_len = backtrace(new_alloc->bt, BT_MAX);
+ DO_ASSERT(mtx_lock(&allocation_mtx) == thrd_success);
+ new_alloc->next = allocation_ll;
+ allocation_ll = new_alloc;
+ DO_ASSERT(mtx_unlock(&allocation_mtx) == thrd_success);
+}
+static void* MALLOC(size_t len, const char* struct_name) {
+ void* res = __real_malloc(len);
+ new_allocation(res, struct_name);
+ return res;
+}
+void __real_free(void* ptr);
+static void alloc_freed(void* ptr) {
+ allocation* p = NULL;
+ DO_ASSERT(mtx_lock(&allocation_mtx) == thrd_success);
+ allocation* it = allocation_ll;
+ while (it->ptr != ptr) {
+ p = it; it = it->next;
+ if (it == NULL) {
+ fprintf(stderr, "Tried to free unknown pointer %p at:\\n", ptr);
+ void* bt[BT_MAX];
+ int bt_len = backtrace(bt, BT_MAX);
+ backtrace_symbols_fd(bt, bt_len, STDERR_FILENO);
+ fprintf(stderr, "\\n\\n");
+ DO_ASSERT(mtx_unlock(&allocation_mtx) == thrd_success);
+ return; // addrsan should catch malloc-unknown and print more info than we have
+ }
+ }
+ if (p) { p->next = it->next; } else { allocation_ll = it->next; }
+ DO_ASSERT(mtx_unlock(&allocation_mtx) == thrd_success);
+ DO_ASSERT(it->ptr == ptr);
+ __real_free(it);
+}
+static void FREE(void* ptr) {
+ if ((long)ptr < 1024) return; // Rust loves to create pointers to the NULL page for dummys
+ alloc_freed(ptr);
+ __real_free(ptr);
+}
+
+void* __wrap_malloc(size_t len) {
+ void* res = __real_malloc(len);
+ new_allocation(res, "malloc call");
+ return res;
+}
+void* __wrap_calloc(size_t nmemb, size_t len) {
+ void* res = __real_calloc(nmemb, len);
+ new_allocation(res, "calloc call");
+ return res;
+}
+void __wrap_free(void* ptr) {
+ if (ptr == NULL) return;
+ alloc_freed(ptr);
+ __real_free(ptr);
+}
+
+void* __real_realloc(void* ptr, size_t newlen);
+void* __wrap_realloc(void* ptr, size_t len) {
+ if (ptr != NULL) alloc_freed(ptr);
+ void* res = __real_realloc(ptr, len);
+ new_allocation(res, "realloc call");
+ return res;
+}
+void __wrap_reallocarray(void* ptr, size_t new_sz) {
+ // Rust doesn't seem to use reallocarray currently
+ assert(false);
+}
+
+void __attribute__((destructor)) check_leaks() {
+ for (allocation* a = allocation_ll; a != NULL; a = a->next) {
+ fprintf(stderr, "%s %p remains:\\n", a->struct_name, a->ptr);
+ backtrace_symbols_fd(a->bt, a->bt_len, STDERR_FILENO);
+ fprintf(stderr, "\\n\\n");
+ }
+ DO_ASSERT(allocation_ll == NULL);
+}
+"""
+
+c_file = ""
+def write_c(s):
+ global c_file
+ c_file += s
+