class Target(Enum):
CSHARP = 1,
+def first_to_lower(string: str) -> str:
+ first = string[0]
+ return first.lower() + string[1:]
+
+def safe_arg_name(arg_name):
+ return "_" + arg_name if arg_name == "lock" or arg_name == "event" or arg_name == "params" else arg_name
+
+def arg_name_repl(s, arg_name):
+ return s.replace(arg_name, "_" + arg_name) if arg_name == "lock" or arg_name == "event" or arg_name == "params" else s
+
class Consts:
def __init__(self, DEBUG: bool, target: Target, **kwargs):
self.target = target
uint32_t = ['int'],
uint64_t = ['long'],
int64_t = ['long'],
+ double = ['double'],
)
self.java_type_map = dict(
String = "string"
self.bindings_header = """
using org.ldk.enums;
using org.ldk.impl;
+using System;
using System.Runtime.InteropServices;
namespace org { namespace ldk { namespace impl {
internal class bindings {
+ internal class ArrayCoder : ICustomMarshaler {
+ int size = 0;
+ GCHandle pinnedArray;
+ public static ICustomMarshaler GetInstance(string pstrCookie) {
+ return new ArrayCoder();
+ }
+
+ public Object MarshalNativeToManaged(IntPtr pNativeData) { throw new NotImplementedException(); }
+ public IntPtr MarshalManagedToNative(Object obj) {
+ if (obj.GetType() == typeof(byte[])) {
+ byte[] inp = (byte[])obj;
+ IntPtr data = Marshal.AllocHGlobal(inp.Length + 8);
+ Marshal.WriteInt64(data, inp.Length);
+ Marshal.Copy(inp, 0, data + 8, inp.Length);
+ this.size = inp.Length + 8;
+ return data;
+ } else {
+ throw new NotImplementedException();
+ }
+ }
+ public void CleanUpNativeData(IntPtr pNativeData) {
+ Marshal.FreeHGlobal(pNativeData);
+ }
+ public void CleanUpManagedData(Object ManagedObj) { }
+ public int GetNativeDataSize() {
+ // Blindly guess based on the last allocation, no idea how else to implement this.
+ return this.size;
+ }
+ }
+
/*static {
init(java.lang.Enum.class, VecOrSliceDef.class);
init_class_cache();
self.c_file_pfx = self.c_file_pfx + "#include <stdio.h>\n#define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__)\n"
if not DEBUG or sys.platform == "darwin":
- self.c_file_pfx = self.c_file_pfx + """#define MALLOC(a, _) malloc(a)
+ self.c_file_pfx = self.c_file_pfx + """#define do_MALLOC(a, _b, _c) malloc(a)
+#define MALLOC(a, _) malloc(a)
#define FREE(p) if ((uint64_t)(p) > 4096) { free(p); }
#define CHECK_ACCESS(p)
#define CHECK_INNER_FIELD_ACCESS_OR_NULL(v)
allocation_ll = new_alloc;
DO_ASSERT(!pthread_mutex_unlock(&allocation_mtx));
}
-static void* MALLOC(size_t len, const char* struct_name) {
+static void* do_MALLOC(size_t len, const char* struct_name, int lineno) {
void* res = __real_malloc(len);
- new_allocation(res, struct_name, len);
+ new_allocation(res, struct_name, lineno);
return res;
}
+#define MALLOC(len, struct_name) do_MALLOC(len, struct_name, __LINE__)
+
void __real_free(void* ptr);
static void alloc_freed(void* ptr) {
allocation* p = NULL;
_Static_assert(sizeof(void*) <= 8, "Pointers must fit into 64 bits");
-typedef jlongArray int64_tArray;
-typedef jbyteArray int8_tArray;
-
-static inline jstring str_ref_to_java(JNIEnv *env, const char* chars, size_t len) {
- // Sadly we need to create a temporary because Java can't accept a char* without a 0-terminator
- char* conv_buf = MALLOC(len + 1, "str conv buf");
- memcpy(conv_buf, chars, len);
- conv_buf[len] = 0;
- jstring ret = (*env)->NewStringUTF(env, conv_buf);
- FREE(conv_buf);
- return ret;
+#define DECL_ARR_TYPE(ty, name) \\
+ struct name##array { \\
+ uint64_t arr_len; /* uint32_t would suffice but we want to align uint64_ts as well */ \\
+ ty elems[]; \\
+ }; \\
+ typedef struct name##array * name##Array; \\
+ static inline name##Array init_##name##Array(size_t arr_len, int lineno) { \\
+ name##Array arr = (name##Array)do_MALLOC(arr_len * sizeof(ty) + sizeof(uint64_t), #name" array init", lineno); \\
+ arr->arr_len = arr_len; \\
+ return arr; \\
+ }
+
+DECL_ARR_TYPE(int64_t, int64_t);
+DECL_ARR_TYPE(uint64_t, uint64_t);
+DECL_ARR_TYPE(int8_t, int8_t);
+DECL_ARR_TYPE(int16_t, int16_t);
+DECL_ARR_TYPE(uint32_t, uint32_t);
+DECL_ARR_TYPE(void*, ptr);
+DECL_ARR_TYPE(char, char);
+typedef charArray jstring;
+
+static inline jstring str_ref_to_cs(const char* chars, size_t len) {
+ charArray arr = init_charArray(len, __LINE__);
+ memcpy(arr->elems, chars, len);
+ return arr;
}
-static inline LDKStr java_to_owned_str(JNIEnv *env, jstring str) {
- uint64_t str_len = (*env)->GetStringUTFLength(env, str);
- char* newchars = MALLOC(str_len + 1, "String chars");
- const char* jchars = (*env)->GetStringUTFChars(env, str, NULL);
- memcpy(newchars, jchars, str_len);
- newchars[str_len] = 0;
- (*env)->ReleaseStringUTFChars(env, str, jchars);
+static inline LDKStr str_ref_to_owned_c(const jstring str) {
+ char* newchars = MALLOC(str->arr_len + 1, "String chars");
+ memcpy(newchars, str->elems, str->arr_len);
+ newchars[str->arr_len] = 0;
LDKStr res = {
.chars = newchars,
- .len = str_len,
+ .len = str->arr_len,
.chars_is_owned = true
};
return res;
}
-const char* CS_LDK_get_ldk_c_bindings_version() {
- return str_ref_to_java(check_get_ldk_bindings_version(), strlen(check_get_ldk_bindings_version()));
+jstring CS_LDK_get_ldk_c_bindings_version() {
+ return str_ref_to_cs(check_get_ldk_bindings_version(), strlen(check_get_ldk_bindings_version()));
}
-const char* CS_LDK_get_ldk_version() {
- return str_ref_to_java(check_get_ldk_version(), strlen(check_get_ldk_version()));
+jstring CS_LDK_get_ldk_version() {
+ return str_ref_to_cs(check_get_ldk_version(), strlen(check_get_ldk_version()));
}
#include "version.c"
"""
self.usize_c_ty = "int64_t"
self.usize_native_ty = "long"
self.native_zero_ptr = "0"
- self.result_c_ty = "jclass"
- self.ptr_arr = "jobjectArray"
+ self.unitary_enum_c_ty = "int32_t"
+ self.ptr_arr = "ptrArray"
self.is_arr_some_check = ("", " != NULL")
- self.get_native_arr_len_call = ("(*env)->GetArrayLength(env, ", ")")
+ self.get_native_arr_len_call = ("", "->arr_len")
self.bindings_footer_wip = "\tstatic bindings() {\n"
def bindings_footer(self):
def c_fn_name_define_pfx(self, fn_name, have_args):
return " CS_LDK_" + fn_name + "("
- def construct_jenv(self):
- res = "JNIEnv *env;\n"
- res += "jint get_jenv_res = (*j_calls->vm)->GetEnv(j_calls->vm, (void**)&env, JNI_VERSION_1_6);\n"
- res += "if (get_jenv_res == JNI_EDETACHED) {\n"
- res += "\tDO_ASSERT((*j_calls->vm)->AttachCurrentThread(j_calls->vm, (void**)&env, NULL) == JNI_OK);\n"
- res += "} else {\n"
- res += "\tDO_ASSERT(get_jenv_res == JNI_OK);\n"
- res += "}\n"
- return res
- def deconstruct_jenv(self):
- res = "if (get_jenv_res == JNI_EDETACHED) {\n"
- res += "\tDO_ASSERT((*j_calls->vm)->DetachCurrentThread(j_calls->vm) == JNI_OK);\n"
- res += "}\n"
- return res
-
def release_native_arr_ptr_call(self, ty_info, arr_var, arr_ptr_var):
- if ty_info.subty is None or not ty_info.subty.c_ty.endswith("Array"):
- return "(*env)->ReleasePrimitiveArrayCritical(env, " + arr_var + ", " + arr_ptr_var + ", 0)"
return None
def create_native_arr_call(self, arr_len, ty_info):
- if ty_info.c_ty == "int8_tArray":
- return "(*env)->NewByteArray(env, " + arr_len + ")"
- elif ty_info.subty.c_ty.endswith("Array"):
- clz_var = ty_info.java_fn_ty_arg[1:].replace("[", "arr_of_")
- self.c_array_class_caches.add(clz_var)
- return "(*env)->NewObjectArray(env, " + arr_len + ", " + clz_var + "_clz, NULL);\n"
- else:
- return "(*env)->New" + ty_info.java_ty.strip("[]").title() + "Array(env, " + arr_len + ")"
+ if ty_info.c_ty == "ptrArray":
+ assert ty_info.rust_obj == "LDKCVec_U5Z" or (ty_info.subty is not None and (ty_info.subty.c_ty.endswith("Array") or ty_info.subty.rust_obj == "LDKStr"))
+ return "init_" + ty_info.c_ty + "(" + arr_len + ", __LINE__)"
def set_native_arr_contents(self, arr_name, arr_len, ty_info):
if ty_info.c_ty == "int8_tArray":
- return ("(*env)->SetByteArrayRegion(env, " + arr_name + ", 0, " + arr_len + ", ", ")")
+ return ("memcpy(" + arr_name + "->elems, ", ", " + arr_len + ")")
elif ty_info.c_ty == "int16_tArray":
- return ("(*env)->SetByteArrayRegion(env, " + arr_name + ", 0, " + arr_len + ", ", ")")
+ return ("memcpy(" + arr_name + "->elems, ", ", " + arr_len + " * 2)")
else:
assert False
def get_native_arr_contents(self, arr_name, dest_name, arr_len, ty_info, copy):
- if ty_info.c_ty == "int8_tArray":
+ if ty_info.c_ty == "int8_tArray" or ty_info.c_ty == "int16_tArray":
if copy:
- return "(*env)->GetByteArrayRegion(env, " + arr_name + ", 0, " + arr_len + ", " + dest_name + ")"
- else:
- return "(*env)->GetByteArrayElements (env, " + arr_name + ", NULL)"
- elif not ty_info.java_ty[:len(ty_info.java_ty) - 2].endswith("[]"):
- return "(*env)->Get" + ty_info.subty.java_ty.title() + "ArrayElements (env, " + arr_name + ", NULL)"
+ byte_len = arr_len
+ if ty_info.c_ty == "int16_tArray":
+ byte_len = arr_len + " * 2"
+ return "memcpy(" + dest_name + ", " + arr_name + "->elems, " + byte_len + "); FREE(" + arr_name + ")"
+ assert not copy
+ if ty_info.c_ty == "ptrArray":
+ return "(void*) " + arr_name + "->elems"
else:
- return None
+ return arr_name + "->elems"
def get_native_arr_elem(self, arr_name, idxc, ty_info):
- if self.get_native_arr_contents(arr_name, "", "", ty_info, False) is None:
- return "(*env)->GetObjectArrayElement(env, " + arr_name + ", " + idxc + ")"
- else:
- assert False # Only called if above is None
+ assert False # Only called if above is None
def get_native_arr_ptr_call(self, ty_info):
- if ty_info.subty is not None and ty_info.subty.c_ty.endswith("Array"):
- return None
- return ("(*env)->GetPrimitiveArrayCritical(env, ", ", NULL)")
+ if ty_info.subty is not None:
+ return "(" + ty_info.subty.c_ty + "*)(((uint8_t*)", ") + 8)"
+ return "(" + ty_info.c_ty + "*)(((uint8_t*)", ") + 8)"
def get_native_arr_entry_call(self, ty_info, arr_name, idxc, entry_access):
- if ty_info.subty is None or not ty_info.subty.c_ty.endswith("Array"):
- return None
- return "(*env)->SetObjectArrayElement(env, " + arr_name + ", " + idxc + ", " + entry_access + ")"
+ return None
def cleanup_native_arr_ref_contents(self, arr_name, dest_name, arr_len, ty_info):
if ty_info.c_ty == "int8_tArray":
- return "(*env)->ReleaseByteArrayElements(env, " + arr_name + ", (int8_t*)" + dest_name + ", 0);"
+ return "FREE(" + arr_name + ");"
else:
- return "(*env)->Release" + ty_info.java_ty.strip("[]").title() + "ArrayElements(env, " + arr_name + ", " + dest_name + ", 0)"
+ return "FREE(" + arr_name + ")"
- def map_hu_array_elems(self, arr_name, conv_name, arr_ty, elem_ty):
+ def map_hu_array_elems(self, arr_name, conv_name, arr_ty, elem_ty, is_nullable):
if elem_ty.java_hu_ty == "UInt5":
return arr_name + " != null ? InternalUtils.convUInt5Array(" + arr_name + ") : null"
elif elem_ty.java_hu_ty == "WitnessVersion":
return arr_name + " != null ? InternalUtils.mapArray(" + arr_name + ", " + conv_name + " => " + elem_ty.from_hu_conv[0] + ") : null"
def str_ref_to_native_call(self, var_name, str_len):
- return "str_ref_to_java(env, " + var_name + ", " + str_len + ")"
+ return "str_ref_to_cs(" + var_name + ", " + str_len + ")"
def str_ref_to_c_call(self, var_name):
- return "java_to_owned_str(env, " + var_name + ")"
+ return "str_ref_to_owned_c(" + var_name + ")"
def str_to_hu_conv(self, var_name):
return None
def str_from_hu_conv(self, var_name):
out_c = out_c + "static void " + struct_name + "_JCalls_free(void* this_arg) {\n"
out_c = out_c + "\t" + struct_name + "_JCalls *j_calls = (" + struct_name + "_JCalls*) this_arg;\n"
out_c = out_c + "\tif (atomic_fetch_sub_explicit(&j_calls->refcnt, 1, memory_order_acquire) == 1) {\n"
- out_c += "\t\t" + self.construct_jenv().replace("\n", "\n\t\t").strip() + "\n"
out_c = out_c + "\t\t(*env)->DeleteWeakGlobalRef(env, j_calls->o);\n"
- out_c += "\t\t" + self.deconstruct_jenv().replace("\n", "\n\t\t").strip() + "\n"
out_c = out_c + "\t\tFREE(j_calls);\n"
out_c = out_c + "\t}\n}\n"
out_c = out_c + ") {\n"
out_c = out_c + "\t" + struct_name + "_JCalls *j_calls = (" + struct_name + "_JCalls*) this_arg;\n"
- out_c += "\t" + self.construct_jenv().replace("\n", "\n\t").strip() + "\n"
for arg_info in fn_line.args_ty:
if arg_info.ret_conv is not None:
if fn_line.ret_ty_info.arg_conv is not None:
out_c += "\t" + fn_line.ret_ty_info.arg_conv.replace("\n", "\n\t") + "\n"
- out_c += "\t" + self.deconstruct_jenv().replace("\n", "\n\t").strip() + "\n"
out_c += "\treturn " + fn_line.ret_ty_info.arg_conv_name + ";\n"
else:
- out_c += "\t" + self.deconstruct_jenv().replace("\n", "\n\t").strip() + "\n"
if not fn_line.ret_ty_info.passed_as_ptr and fn_line.ret_ty_info.c_ty != "void":
out_c += "\treturn ret;\n"
out_c += "\t" + struct_name + " *obj = (" + struct_name + "*)untag_ptr(ptr);\n"
out_c += f"\tassert(obj->tag == {struct_name}_{var.var_name});\n"
if field_map.ret_conv is not None:
- out_c += ("\t\t\t" + field_map.ret_conv[0].replace("\n", "\n\t\t\t"))
+ out_c += ("\t" + field_map.ret_conv[0].replace("\n", "\n\t"))
if var.tuple_variant:
out_c += "obj->" + camel_to_snake(var.var_name)
else:
return java_hu_struct
def map_function(self, argument_types, c_call_string, method_name, meth_n, return_type_info, struct_meth, default_constructor_args, takes_self, takes_self_as_ref, args_known, type_mapping_generator, doc_comment):
- arg_name_repl = lambda s, arg_name: s.replace(arg_name, "_" + arg_name) if arg_name == "lock" or arg_name == "event" or arg_name == "params" else s
out_java = ""
out_c = ""
out_java_struct = None
out_c += (", ")
if arg_conv_info.c_ty != "void":
out_c += (arg_conv_info.c_ty + " " + arg_conv_info.arg_name)
+ if "[]" in arg_conv_info.java_ty:
+ out_java += "[MarshalAs(UnmanagedType.CustomMarshaler, MarshalType=\"org.ldk.impl.ArrayCoder\")] "
out_java += (arg_conv_info.java_ty + " _" + arg_conv_info.arg_name) # Add a _ to avoid using reserved words
out_java_struct = ""