import sys
class Target(Enum):
- CSHARP = 1,
+ WINDOWS = 1,
+ LINUX = 2,
+ PTHREAD = 3,
def first_to_lower(string: str) -> str:
first = string[0]
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
+ if arg_name == "lock" or arg_name == "event" or arg_name == "params":
+ names = [i for i in range(len(s)-1, -1, -1) if s.startswith(arg_name, i)]
+ separators = set([",", "(", ")", " ", "."])
+ for idx in names:
+ match = idx == 0 or s[idx - 1] in separators
+ match &= idx + len(arg_name) == len(s) or s[idx + len(arg_name)] in separators
+ if match:
+ s = s[0:idx] + "_" + s[idx:]
+ return s
+ return s
class Consts:
def __init__(self, DEBUG: bool, target: Target, outdir: str, **kwargs):
}
}"""
+ self.witness_program_defn = """public class WitnessProgram : CommonBase {
+ /** The witness program bytes themselves */
+ public readonly byte[] program;
+ /** The witness version */
+ public readonly WitnessVersion version;
+
+ internal WitnessProgram(object _dummy, long ptr) : base(ptr) {
+ this.program = InternalUtils.decodeUint8Array(bindings.WitnessProgram_get_program(ptr));
+ this.version = new WitnessVersion(bindings.WitnessProgram_get_version(ptr));
+ }
+ static private long check_args(byte[] program, WitnessVersion version) {
+ if (program.Length < 2 || program.Length > 40) throw new ArgumentException();
+ if (version.getVal() == 0 && program.Length != 20 && program.Length != 32) throw new ArgumentException();
+ return InternalUtils.encodeUint8Array(program);
+ }
+ public WitnessProgram(byte[] program, WitnessVersion version) :
+ this(null, bindings.WitnessProgram_new(version.getVal(), check_args(program, version))) {}
+
+ ~WitnessProgram() {
+ if (ptr != 0) { bindings.WitnessProgram_free(ptr); }
+ }
+}"""
self.c_file_pfx = """
// On OSX jlong (ie long long) is not equivalent to int64_t, so we override here
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":
+ if self.target == Target.WINDOWS:
+ self.c_file_pfx = self.c_file_pfx + """#include <heapapi.h>
+static HANDLE process_heap = NULL;
+static inline void* init_heap() {
+ if (UNLIKELY(process_heap == NULL)) {
+ // Assume pointer writes wont tear, which is true where we need it.
+ process_heap = GetProcessHeap();
+ }
+}
+static inline void* MALLOC(size_t a, const char* _) {
+ init_heap();
+ return HeapAlloc(process_heap, HEAP_ZERO_MEMORY, a);
+}
+#define do_MALLOC(a, b, _c) MALLOC(a, b)
+#define FREE(p) if ((uint64_t)(p) > 4096) { init_heap(); HeapFree(process_heap, 0, p); }
+#define CHECK_ACCESS(p)
+#define CHECK_INNER_FIELD_ACCESS_OR_NULL(v)
+"""
+ elif not DEBUG or self.target != Target.LINUX:
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); }
}
"""
- if sys.platform != "darwin":
+ if self.target == Target.LINUX:
self.c_file_pfx += """
// 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
return res;
}
-typedef bool jboolean;
+// The C# Bool marshalling is defined as 4 bytes, but the size of bool is platform-dependent
+typedef int32_t jboolean;
int64_t CS_LDK_allocate_buffer(int64_t len) {
return (int64_t)MALLOC(len, "C#-requested buffer");
return "InternalUtils.getArrayLength(" + arr_name + ")"
def get_java_arr_elem(self, elem_ty, arr_name, idx):
- if elem_ty.c_ty == "int64_t" or elem_ty.c_ty == "uint64_t" or elem_ty.c_ty.endswith("Array") or elem_ty.c_ty == "uintptr_t":
+ if elem_ty.c_ty == "int64_t" or elem_ty.c_ty == "uint64_t":
+ return "InternalUtils.getU64ArrayElem(" + arr_name + ", " + idx + ")"
+ elif elem_ty.c_ty.endswith("Array") or elem_ty.c_ty == "uintptr_t" or elem_ty.rust_obj == "LDKStr":
return "InternalUtils.getU64ArrayElem(" + arr_name + ", " + idx + ")"
elif elem_ty.rust_obj == "LDKU5":
return "InternalUtils.getU8ArrayElem(" + arr_name + ", " + idx + ")"
- elif elem_ty.rust_obj == "LDKStr":
- return "InternalUtils.getU32ArrayElem(" + arr_name + ", " + idx + ")"
else:
assert False
out_c += f"\t\tcase {struct_name}_{var.var_name}: return {var_idx};\n"
hu_conv_body = ""
for idx, (field_ty, field_docs) in enumerate(var.fields):
+ arg_name = safe_arg_name(field_ty.arg_name)
if field_docs is not None:
java_hu_subclasses += "\t\t/**\n\t\t * " + field_docs.replace("\n", "\n\t\t * ") + "\n\t\t */\n"
- java_hu_subclasses += f"\t\tpublic {field_ty.java_hu_ty} {field_ty.arg_name};\n"
+ java_hu_subclasses += f"\t\tpublic {field_ty.java_hu_ty} {arg_name};\n"
if field_ty.to_hu_conv is not None:
- hu_conv_body += f"\t\t\t{field_ty.java_ty} {field_ty.arg_name} = bindings.{struct_name}_{var.var_name}_get_{field_ty.arg_name}(ptr);\n"
- hu_conv_body += f"\t\t\t" + field_ty.to_hu_conv.replace("\n", "\n\t\t\t") + "\n"
- hu_conv_body += f"\t\t\tthis." + field_ty.arg_name + " = " + field_ty.to_hu_conv_name + ";\n"
+ hu_conv_body += f"\t\t\t{field_ty.java_ty} {arg_name} = bindings.{struct_name}_{var.var_name}_get_{field_ty.arg_name}(ptr);\n"
+ hu_conv_body += f"\t\t\t" + arg_name_repl(field_ty.to_hu_conv.replace("\n", "\n\t\t\t"), field_ty.arg_name) + "\n"
+ hu_conv_body += f"\t\t\tthis." + arg_name + " = " + arg_name_repl(field_ty.to_hu_conv_name, field_ty.arg_name) + ";\n"
else:
- hu_conv_body += f"\t\t\tthis.{field_ty.arg_name} = bindings.{struct_name}_{var.var_name}_get_{field_ty.arg_name}(ptr);\n"
+ hu_conv_body += f"\t\t\tthis.{arg_name} = bindings.{struct_name}_{var.var_name}_get_{arg_name}(ptr);\n"
java_hu_subclasses += "\t\tinternal " + java_hu_type + "_" + var.var_name + "(long ptr) : base(null, ptr) {\n"
java_hu_subclasses += hu_conv_body
java_hu_subclasses += "\t\t}\n\t}\n"
out_opaque_struct_human = ""
out_opaque_struct_human += self.hu_struct_file_prefix
out_opaque_struct_human += "\n/**\n * " + struct_doc_comment.replace("\n", "\n * ") + "\n */\n"
- hu_name = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple").replace("LDK", "")
+ hu_name = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple").replace("LDKC4Tuple", "FourTuple").replace("LDK", "")
out_opaque_struct_human += ("public class " + hu_name + " : CommonBase")
if struct_name.startswith("LDKLocked") or struct_name.startswith("LDKReadOnly"):
out_opaque_struct_human += (", IDisposable")
}}
}}
public delegate {jret} {fn_suffix}_callback(int obj_ptr, int fn_id{jargs});
+ static {fn_suffix}_callback {fn_suffix}_callback_inst = c_callback_{fn_suffix};
""")
bindings.write(self.native_meth_decl(f"register_{fn_suffix}_invoker", "int") + f"({fn_suffix}_callback callee);\n")
# Easiest way to get a static run is just define a variable, even if we dont care
- bindings.write(f"\tstatic int _run_{fn_suffix}_registration = register_{fn_suffix}_invoker(c_callback_{fn_suffix});")
+ bindings.write(f"\tstatic int _run_{fn_suffix}_registration = register_{fn_suffix}_invoker({fn_suffix}_callback_inst);")
bindings.write("""
}