X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=typescript_strings.py;h=102d2256798cc7d3d80ccc46459c7ff7867b682e;hb=254448caed0c5708ffd414a2b418983792a75a3c;hp=892c63b802488a20b73b7101fa348e11b58afaee;hpb=1a849ce9a84a17a58ba0e19cb60dfdf95242356b;p=ldk-java diff --git a/typescript_strings.py b/typescript_strings.py index 892c63b8..102d2256 100644 --- a/typescript_strings.py +++ b/typescript_strings.py @@ -17,10 +17,10 @@ class Consts: self.function_ptr_counter = 0 self.function_ptrs = {} self.c_type_map = dict( - uint8_t = ['number', 'Uint8Array'], - uint16_t = ['number', 'Uint16Array'], - uint32_t = ['number', 'Uint32Array'], - uint64_t = ['BigInt'], + uint8_t = ['number', 'number', 'Uint8Array'], + uint16_t = ['number', 'number', 'Uint16Array'], + uint32_t = ['number', 'number', 'Uint32Array'], + uint64_t = ['bigint', 'bigint', 'BigUint64Array'], ) self.java_type_map = dict( String = "number" @@ -29,22 +29,259 @@ class Consts: String = "string" ) - self.wasm_decoding_map = dict( - int8_tArray = 'decodeUint8Array' - ) - - self.wasm_encoding_map = dict( - int8_tArray = 'encodeUint8Array', - ) - self.to_hu_conv_templates = dict( ptr = 'const {var_name}_hu_conv: {human_type} = new {human_type}(null, {var_name});', default = 'const {var_name}_hu_conv: {human_type} = new {human_type}(null, {var_name});', ) - self.bindings_header = self.wasm_import_header(target) + self.bindings_header = """ +import * as version from './version.mjs'; +import { UInt5 } from './structs/CommonBase.mjs'; + +const imports: any = {}; +imports.env = {}; + +var js_objs: Array> = []; +var js_invoke: Function; +var getRandomValues: Function; + +imports.wasi_snapshot_preview1 = { + "fd_write": (fd: number, iovec_array_ptr: number, iovec_array_len: number) => { + // This should generally only be used to print panic messages + console.log("FD_WRITE to " + fd + " in " + iovec_array_len + " chunks."); + const ptr_len_view = new Uint32Array(wasm.memory.buffer, iovec_array_ptr, iovec_array_len * 2); + for (var i = 0; i < iovec_array_len; i++) { + const bytes_view = new Uint8Array(wasm.memory.buffer, ptr_len_view[i*2], ptr_len_view[i*2+1]); + console.log(String.fromCharCode(...bytes_view)); + } + return 0; + }, + "fd_close": (_fd: number) => { + // This is not generally called, but may be referenced in debug builds + console.log("wasi_snapshot_preview1:fd_close"); + return 58; // Not Supported + }, + "fd_seek": (_fd: number, _offset: bigint, _whence: number, _new_offset: number) => { + // This is not generally called, but may be referenced in debug builds + console.log("wasi_snapshot_preview1:fd_seek"); + return 58; // Not Supported + }, + "random_get": (buf_ptr: number, buf_len: number) => { + const buf = new Uint8Array(wasm.memory.buffer, buf_ptr, buf_len); + getRandomValues(buf); + return 0; + }, + "environ_sizes_get": (environ_var_count_ptr: number, environ_len_ptr: number) => { + // This is called before fd_write to format + print panic messages + console.log("wasi_snapshot_preview1:environ_sizes_get"); + const out_count_view = new Uint32Array(wasm.memory.buffer, environ_var_count_ptr, 1); + out_count_view[0] = 0; + const out_len_view = new Uint32Array(wasm.memory.buffer, environ_len_ptr, 1); + out_len_view[0] = 0; + return 0; + }, + "environ_get": (environ_ptr: number, environ_buf_ptr: number) => { + // This is called before fd_write to format + print panic messages + console.log("wasi_snapshot_preview1:environ_get"); + return 58; // Note supported - we said there were 0 environment entries! + }, + "proc_exit" : () => { + console.log("wasi_snapshot_preview1:proc_exit"); + }, +}; + +var wasm: any = null; +let isWasmInitialized: boolean = false; + +async function finishInitializeWasm(wasmInstance: WebAssembly.Instance) { + if (typeof crypto === "undefined") { + var crypto_import = (await import('crypto')).webcrypto; + getRandomValues = crypto_import.getRandomValues.bind(crypto_import); + } else { + getRandomValues = crypto.getRandomValues.bind(crypto); + } + + wasm = wasmInstance.exports; + if (!wasm.test_bigint_pass_deadbeef0badf00d(BigInt("0xdeadbeef0badf00d"))) { + throw new Error(\"Currently need BigInt-as-u64 support, try ----experimental-wasm-bigint"); + } + + if (decodeString(wasm.TS_get_lib_version_string()) !== version.get_ldk_java_bindings_version()) + throw new Error(\"Compiled LDK library and LDK class failes do not match\"); + // Fetching the LDK versions from C also checks that the header and binaries match + const c_bindings_ver: number = wasm.TS_get_ldk_c_bindings_version(); + const ldk_ver: number = wasm.TS_get_ldk_version(); + if (c_bindings_ver == 0) + throw new Error(\"LDK version did not match the header we built against\"); + if (ldk_ver == 0) + throw new Error(\"LDK C bindings version did not match the header we built against\"); + const c_bindings_version: string = decodeString(c_bindings_ver) + const ldk_version: string = decodeString(ldk_ver); + console.log(\"Loaded LDK-Java Bindings with LDK \" + ldk_version + \" and LDK-C-Bindings \" + c_bindings_version); + + isWasmInitialized = true; +} + +/* @internal */ +export async function initializeWasmFromUint8Array(wasmBinary: Uint8Array) { + imports.env["js_invoke_function"] = js_invoke; + const { instance: wasmInstance } = await WebAssembly.instantiate(wasmBinary, imports); + await finishInitializeWasm(wasmInstance); +} + +/* @internal */ +export async function initializeWasmFetch(uri: string) { + const stream = fetch(uri); + imports.env["js_invoke_function"] = js_invoke; + const { instance: wasmInstance } = await WebAssembly.instantiateStreaming(stream, imports); + await finishInitializeWasm(wasmInstance); +}""" + + self.bindings_header += """ +// WASM CODEC + +/* @internal */ +export function uint5ArrToBytes(inputArray: Array): Uint8Array { + const arr = new Uint8Array(inputArray.length); + for (var i = 0; i < inputArray.length; i++) { + arr[i] = inputArray[i].getVal(); + } + return arr; +} + +/* @internal */ +export function encodeUint8Array (inputArray: Uint8Array): number { + const cArrayPointer = wasm.TS_malloc(inputArray.length + 4); + const arrayLengthView = new Uint32Array(wasm.memory.buffer, cArrayPointer, 1); + arrayLengthView[0] = inputArray.length; + const arrayMemoryView = new Uint8Array(wasm.memory.buffer, cArrayPointer + 4, inputArray.length); + arrayMemoryView.set(inputArray); + return cArrayPointer; +} +/* @internal */ +export function encodeUint32Array (inputArray: Uint32Array|Array): number { + const cArrayPointer = wasm.TS_malloc((inputArray.length + 1) * 4); + const arrayMemoryView = new Uint32Array(wasm.memory.buffer, cArrayPointer, inputArray.length); + arrayMemoryView.set(inputArray, 1); + arrayMemoryView[0] = inputArray.length; + return cArrayPointer; +} +/* @internal */ +export function encodeUint64Array (inputArray: BigUint64Array|Array): number { + const cArrayPointer = wasm.TS_malloc(inputArray.length * 8 + 1); + const arrayLengthView = new Uint32Array(wasm.memory.buffer, cArrayPointer, 1); + arrayLengthView[0] = inputArray.length; + const arrayMemoryView = new BigUint64Array(wasm.memory.buffer, cArrayPointer + 4, inputArray.length); + arrayMemoryView.set(inputArray); + return cArrayPointer; +} + +/* @internal */ +export function check_arr_len(arr: Uint8Array, len: number): Uint8Array { + if (arr.length != len) { throw new Error("Expected array of length " + len + "got " + arr.length); } + return arr; +} - self.bindings_version_file = "" +/* @internal */ +export function getArrayLength(arrayPointer: number): number { + const arraySizeViewer = new Uint32Array(wasm.memory.buffer, arrayPointer, 1); + return arraySizeViewer[0]; +} +/* @internal */ +export function decodeUint8Array (arrayPointer: number, free = true): Uint8Array { + const arraySize = getArrayLength(arrayPointer); + const actualArrayViewer = new Uint8Array(wasm.memory.buffer, arrayPointer + 4, arraySize); + // Clone the contents, TODO: In the future we should wrap the Viewer in a class that + // will free the underlying memory when it becomes unreachable instead of copying here. + // Note that doing so may have edge-case interactions with memory resizing (invalidating the buffer). + const actualArray = actualArrayViewer.slice(0, arraySize); + if (free) { + wasm.TS_free(arrayPointer); + } + return actualArray; +} +const decodeUint32Array = (arrayPointer: number, free = true) => { + const arraySize = getArrayLength(arrayPointer); + const actualArrayViewer = new Uint32Array( + wasm.memory.buffer, // value + arrayPointer + 4, // offset (ignoring length bytes) + arraySize // uint32 count + ); + // Clone the contents, TODO: In the future we should wrap the Viewer in a class that + // will free the underlying memory when it becomes unreachable instead of copying here. + const actualArray = actualArrayViewer.slice(0, arraySize); + if (free) { + wasm.TS_free(arrayPointer); + } + return actualArray; +} + + +export function freeWasmMemory(pointer: number) { wasm.TS_free(pointer); } + +/* @internal */ +export function getU32ArrayElem(arrayPointer: number, idx: number): number { + const actualArrayViewer = new Uint32Array(wasm.memory.buffer, arrayPointer + 4, idx + 1); + return actualArrayViewer[idx]; +} + +/* @internal */ +export function getU8ArrayElem(arrayPointer: number, idx: number): number { + const actualArrayViewer = new Uint8Array(wasm.memory.buffer, arrayPointer + 4, idx + 1); + return actualArrayViewer[idx]; +} + + +/* @internal */ +export function encodeString(str: string): number { + const charArray = new TextEncoder().encode(str); + return encodeUint8Array(charArray); +} + +/* @internal */ +export function decodeString(stringPointer: number, free = true): string { + const arraySize = getArrayLength(stringPointer); + const memoryView = new Uint8Array(wasm.memory.buffer, stringPointer + 4, arraySize); + const result = new TextDecoder("utf-8").decode(memoryView); + + if (free) { + wasm.TS_free(stringPointer); + } + + return result; +} +""" + if DEBUG: + self.bindings_header += """ +/* @internal */ +export function getRemainingAllocationCount(): number { + return wasm.TS_allocs_remaining(); +} +/* @internal */ +export function debugPrintRemainingAllocs() { + wasm.TS_print_leaks(); +} +""" + else: + self.bindings_header += "\n/* @internal */ export function getRemainingAllocationCount(): number { return 0; }\n" + self.bindings_header += "/* @internal */ export function debugPrintRemainingAllocs() { }\n" + + with open(outdir + "/index.mts", 'a') as index: + index.write("""import { initializeWasmFetch, initializeWasmFromUint8Array } from './bindings.mjs'; +/** Initializes the WASM backend by calling `fetch()` on the given URI - Browser only */ +export async function initializeWasmWebFetch(uri: string) { + await initializeWasmFetch(uri); +} +/** Initializes the WASM backend given a Uint8Array of the .wasm binary file - Browser or Node.JS */ +export async function initializeWasmFromBinary(bin: Uint8Array) { + await initializeWasmFromUint8Array(bin); +} + +""") + + self.bindings_version_file = """export function get_ldk_java_bindings_version(): String { + return ""; +}""" self.bindings_footer = "" @@ -57,7 +294,7 @@ function get_freeer(ptr: number, free_fn: (ptr: number) => void) { } } -export default class CommonBase { +export class CommonBase { protected ptr: number; protected ptrs_to: object[] = []; protected constructor(ptr: number, free_fn: (ptr: number) => void) { @@ -80,22 +317,31 @@ export default class CommonBase { finalizer.unregister(o); } } + +export class UInt5 { + public constructor(private val: number) { + if (val > 32 || val < 0) throw new Error("UInt5 value is out of range"); + } + public getVal(): number { + return this.val; + } +} """ self.txout_defn = """export class TxOut extends CommonBase { /** The script_pubkey in this output */ public script_pubkey: Uint8Array; /** The value, in satoshis, of this output */ - public value: BigInt; + public value: bigint; /* @internal */ public constructor(_dummy: object, ptr: number) { super(ptr, bindings.TxOut_free); - this.script_pubkey = bindings.TxOut_get_script_pubkey(ptr); + this.script_pubkey = bindings.decodeUint8Array(bindings.TxOut_get_script_pubkey(ptr)); this.value = bindings.TxOut_get_value(ptr); } - public constructor_new(value: BigInt, script_pubkey: Uint8Array): TxOut { - return new TxOut(null, bindings.TxOut_new(script_pubkey, value)); + public constructor_new(value: bigint, script_pubkey: Uint8Array): TxOut { + return new TxOut(null, bindings.TxOut_new(bindings.encodeUint8Array(script_pubkey), value)); } }""" self.obj_defined(["TxOut"], "structs") @@ -121,11 +367,12 @@ uint32_t __attribute__((export_name("test_bigint_pass_deadbeef0badf00d"))) test_ """ if not DEBUG: - self.c_file_pfx = self.c_file_pfx + """ + self.c_file_pfx += """ void *malloc(size_t size); void free(void *ptr); #define MALLOC(a, _) malloc(a) +#define do_MALLOC(a, _b, _c) malloc(a) #define FREE(p) if ((unsigned long)(p) > 4096) { free(p); } #define DO_ASSERT(a) (void)(a) #define CHECK(a) @@ -133,7 +380,16 @@ void free(void *ptr); #define CHECK_INNER_FIELD_ACCESS_OR_NULL(v) """ else: - self.c_file_pfx = self.c_file_pfx + """ + self.c_file_pfx += """ +extern int snprintf(char *str, size_t size, const char *format, ...); +typedef int32_t ssize_t; +ssize_t write(int fd, const void *buf, size_t count); +#define DEBUG_PRINT(...) do { \\ + char debug_str[1024]; \\ + int s_len = snprintf(debug_str, 1023, __VA_ARGS__); \\ + write(2, debug_str, s_len); \\ +} while (0); + // 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 @@ -149,43 +405,58 @@ typedef struct allocation { struct allocation* next; void* ptr; const char* struct_name; + int lineno; } allocation; static allocation* allocation_ll = NULL; +static allocation* freed_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) { +extern void* __real_malloc(size_t len); +extern void* __real_calloc(size_t nmemb, size_t len); +extern void* __real_aligned_alloc(size_t alignment, size_t size); +static void new_allocation(void* res, const char* struct_name, int lineno) { allocation* new_alloc = __real_malloc(sizeof(allocation)); new_alloc->ptr = res; new_alloc->struct_name = struct_name; new_alloc->next = allocation_ll; + new_alloc->lineno = lineno; allocation_ll = new_alloc; } -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); + 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) { +static void alloc_freed(void* ptr, int lineno) { allocation* p = NULL; allocation* it = allocation_ll; while (it->ptr != ptr) { p = it; it = it->next; if (it == NULL) { - //XXX: fprintf(stderr, "Tried to free unknown pointer %p\\n", ptr); - return; // addrsan should catch malloc-unknown and print more info than we have + p = NULL; + it = freed_ll; + while (it && it->ptr != ptr) { p = it; it = it->next; } + if (it == NULL) { + DEBUG_PRINT("Tried to free unknown pointer %p at line %d.\\n", ptr, lineno); + } else { + DEBUG_PRINT("Tried to free unknown pointer %p at line %d.\\n Possibly double-free from %s, allocated on line %d.", ptr, lineno, it->struct_name, it->lineno); + } + abort(); } } if (p) { p->next = it->next; } else { allocation_ll = it->next; } DO_ASSERT(it->ptr == ptr); - __real_free(it); + it->next = freed_ll; + freed_ll = it; } -static void FREE(void* ptr) { +static void do_FREE(void* ptr, int lineno) { if ((unsigned long)ptr <= 4096) return; // Rust loves to create pointers to the NULL page for dummys - alloc_freed(ptr); + alloc_freed(ptr, lineno); __real_free(ptr); } +#define FREE(ptr) do_FREE(ptr, __LINE__) static void CHECK_ACCESS(const void* ptr) { allocation* it = allocation_ll; @@ -206,25 +477,30 @@ static void CHECK_ACCESS(const void* ptr) { void* __wrap_malloc(size_t len) { void* res = __real_malloc(len); - new_allocation(res, "malloc call"); + new_allocation(res, "malloc call", 0); return res; } void* __wrap_calloc(size_t nmemb, size_t len) { void* res = __real_calloc(nmemb, len); - new_allocation(res, "calloc call"); + new_allocation(res, "calloc call", 0); + return res; +} +void* __wrap_aligned_alloc(size_t alignment, size_t size) { + void* res = __real_aligned_alloc(alignment, size); + new_allocation(res, "aligned_alloc call", 0); return res; } void __wrap_free(void* ptr) { if (ptr == NULL) return; - alloc_freed(ptr); + alloc_freed(ptr, 0); __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); + if (ptr != NULL) alloc_freed(ptr, 0); void* res = __real_realloc(ptr, len); - new_allocation(res, "realloc call"); + new_allocation(res, "realloc call", 0); return res; } void __wrap_reallocarray(void* ptr, size_t new_sz) { @@ -232,11 +508,17 @@ void __wrap_reallocarray(void* ptr, size_t new_sz) { DO_ASSERT(false); } -void __attribute__((destructor)) check_leaks() { +uint32_t __attribute__((export_name("TS_allocs_remaining"))) allocs_remaining() { + uint32_t count = 0; for (allocation* a = allocation_ll; a != NULL; a = a->next) { - //XXX: fprintf(stderr, "%s %p remains\\n", a->struct_name, a->ptr); + count++; + } + return count; +} +void __attribute__((export_name("TS_print_leaks"))) print_leaks() { + for (allocation* a = allocation_ll; a != NULL; a = a->next) { + DEBUG_PRINT("%s %p remains. Allocated on line %d\\n", a->struct_name, a->ptr, a->lineno); } - DO_ASSERT(allocation_ll == NULL); } """ self.c_file_pfx = self.c_file_pfx + """ @@ -253,8 +535,8 @@ _Static_assert(sizeof(void*) == 4, "Pointers mut be 32 bits"); ty elems[]; \\ }; \\ typedef struct name##array * name##Array; \\ - static inline name##Array init_##name##Array(size_t arr_len) { \\ - name##Array arr = (name##Array)MALLOC(arr_len * sizeof(ty) + sizeof(uint32_t), "##name array init"); \\ + 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(uint32_t), #name" array init", lineno); \\ arr->arr_len = arr_len; \\ return arr; \\ } @@ -267,7 +549,7 @@ DECL_ARR_TYPE(char, char); typedef charArray jstring; static inline jstring str_ref_to_ts(const char* chars, size_t len) { - charArray arr = init_charArray(len); + charArray arr = init_charArray(len, __LINE__); memcpy(arr->elems, chars, len); return arr; } @@ -291,14 +573,27 @@ uint32_t __attribute__((export_name("TS_malloc"))) TS_malloc(uint32_t size) { void __attribute__((export_name("TS_free"))) TS_free(uint32_t ptr) { FREE((void*)ptr); } + +jstring __attribute__((export_name("TS_get_ldk_c_bindings_version"))) TS_get_ldk_c_bindings_version() { + const char *res = check_get_ldk_bindings_version(); + if (res == NULL) return NULL; + return str_ref_to_ts(res, strlen(res)); +} +jstring __attribute__((export_name("TS_get_ldk_version"))) get_ldk_version() { + const char *res = check_get_ldk_version(); + if (res == NULL) return NULL; + return str_ref_to_ts(res, strlen(res)); +} +#include "version.c" """ - self.c_version_file = "" + self.c_version_file = """jstring __attribute__((export_name("TS_get_lib_version_string"))) TS_get_lib_version_string() { + return str_ref_to_ts("", strlen("")); +}""" self.hu_struct_file_prefix = """ -import CommonBase from './CommonBase.mjs'; +import { CommonBase, UInt5 } from './CommonBase.mjs'; import * as bindings from '../bindings.mjs' -import * as InternalUtils from '../InternalUtils.mjs' """ self.util_fn_pfx = self.hu_struct_file_prefix + "\nexport class UtilMethods extends CommonBase {\n" @@ -312,18 +607,12 @@ import * as InternalUtils from '../InternalUtils.mjs' self.is_arr_some_check = ("", " != 0") self.get_native_arr_len_call = ("", "->arr_len") - with open(outdir + "/InternalUtils.mts", "w") as f: - f.write("export function check_arr_len(arr: Uint8Array, len: number): Uint8Array {\n") - f.write("\tif (arr.length != len) { throw new Error(\"Expected array of length \" + len + \"got \" + arr.length); }\n") - f.write("\treturn arr;\n") - f.write("}") - def release_native_arr_ptr_call(self, ty_info, arr_var, arr_ptr_var): return None def create_native_arr_call(self, arr_len, ty_info): if ty_info.c_ty == "ptrArray": - assert ty_info.subty is not None and ty_info.subty.c_ty.endswith("Array") - return "init_" + ty_info.c_ty + "(" + arr_len + ")" + assert ty_info.rust_obj == "LDKCVec_u5Z" or (ty_info.subty is not None and ty_info.subty.c_ty.endswith("Array")) + 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 ("memcpy(" + arr_name + "->elems, ", ", " + arr_len + ")") @@ -332,12 +621,12 @@ import * as InternalUtils from '../InternalUtils.mjs' def get_native_arr_contents(self, arr_name, dest_name, arr_len, ty_info, copy): if ty_info.c_ty == "int8_tArray": if copy: - return "memcpy(" + dest_name + ", " + arr_name + "->elems, " + arr_len + ")" + return "memcpy(" + dest_name + ", " + arr_name + "->elems, " + arr_len + "); FREE(" + arr_name + ")" if ty_info.c_ty == "ptrArray": - return "(void*) " + arr_name + "->elems" + return "(void*) " + arr_name + "->elems /* XXX " + arr_name + " leaks */" else: assert not copy - return arr_name + "->elems" + return arr_name + "->elems /* XXX " + arr_name + " leaks */" def get_native_arr_elem(self, arr_name, idxc, ty_info): assert False # Only called if above is None def get_native_arr_ptr_call(self, ty_info): @@ -353,6 +642,9 @@ import * as InternalUtils from '../InternalUtils.mjs' return None def map_hu_array_elems(self, arr_name, conv_name, arr_ty, elem_ty): + if elem_ty.rust_obj == "LDKu5": + return arr_name + " != null ? bindings.uint5ArrToBytes(" + arr_name + ") : null" + assert elem_ty.c_ty == "uint32_t" or elem_ty.c_ty.endswith("Array") return arr_name + " != null ? " + arr_name + ".map(" + conv_name + " => " + elem_ty.from_hu_conv[0] + ") : null" def str_ref_to_native_call(self, var_name, str_len): @@ -367,180 +659,50 @@ import * as InternalUtils from '../InternalUtils.mjs' def c_fn_name_define_pfx(self, fn_name, have_args): return " __attribute__((export_name(\"TS_" + fn_name + "\"))) TS_" + fn_name + "(" - def wasm_import_header(self, target): - res = """ -const imports: any = {}; -imports.env = {}; - -var js_objs: Array> = []; -var js_invoke: Function; - -imports.wasi_snapshot_preview1 = { - "fd_write": (fd: number, iovec_array_ptr: number, iovec_array_len: number) => { - // This should generally only be used to print panic messages - console.log("FD_WRITE to " + fd + " in " + iovec_array_len + " chunks."); - const ptr_len_view = new Uint32Array(wasm.memory.buffer, iovec_array_ptr, iovec_array_len * 2); - for (var i = 0; i < iovec_array_len; i++) { - const bytes_view = new Uint8Array(wasm.memory.buffer, ptr_len_view[i*2], ptr_len_view[i*2+1]); - console.log(String.fromCharCode(...bytes_view)); - } - return 0; - }, - "random_get": (buf_ptr: number, buf_len: number) => { - const buf = new Uint8Array(wasm.memory.buffer, buf_ptr, buf_len); - crypto.getRandomValues(buf); - return 0; - }, - "environ_sizes_get": (environ_var_count_ptr: number, environ_len_ptr: number) => { - // This is called before fd_write to format + print panic messages - console.log("wasi_snapshot_preview1:environ_sizes_get"); - const out_count_view = new Uint32Array(wasm.memory.buffer, environ_var_count_ptr, 1); - out_count_view[0] = 1; - const out_len_view = new Uint32Array(wasm.memory.buffer, environ_len_ptr, 1); - out_len_view[0] = "RUST_BACKTRACE=1".length + 1; // Note that string must be NULL-terminated - return 0; - }, - "environ_get": (environ_ptr: number, environ_buf_ptr: number) => { - // This is called before fd_write to format + print panic messages - console.log("wasi_snapshot_preview1:environ_get"); - const out_ptrs = new Uint32Array(wasm.memory.buffer, environ_ptr, 2); - out_ptrs[0] = environ_buf_ptr; - out_ptrs[1] = "RUST_BACKTRACE=1".length; - const out_environ = new Uint8Array(wasm.memory.buffer, environ_buf_ptr, out_ptrs[1]); - for (var i = 0; i < out_ptrs[1]; i++) { out_environ[i] = "RUST_BACKTRACE=1".codePointAt(i); } - out_environ[out_ptrs[1]] = 0; - return 0; - }, - "proc_exit" : () => { - console.log("wasi_snapshot_preview1:proc_exit"); - }, -}; - -var wasm: any = null; -let isWasmInitialized: boolean = false; -""" - - if target == Target.NODEJS: - res += """import * as fs from 'fs'; -import { webcrypto as crypto } from 'crypto'; -export async function initializeWasm(path: string) { - const source = fs.readFileSync(path); - imports.env["js_invoke_function"] = js_invoke; - const { instance: wasmInstance } = await WebAssembly.instantiate(source, imports); - wasm = wasmInstance.exports; - if (!wasm.test_bigint_pass_deadbeef0badf00d(BigInt("0xdeadbeef0badf00d"))) { - throw new Error(\"Currently need BigInt-as-u64 support, try ----experimental-wasm-bigint"); - } - isWasmInitialized = true; -}; -""" - else: - res += """ -export async function initializeWasm(uri: string) { - const stream = fetch(uri); - imports.env["js_invoke_function"] = js_invoke; - const { instance: wasmInstance } = await WebAssembly.instantiateStreaming(stream, imports); - wasm = wasmInstance.exports; - if (!wasm.test_bigint_pass_deadbeef0badf00d(BigInt("0xdeadbeef0badf00d"))) { - throw new Error(\"Currently need BigInt-as-u64 support, try ----experimental-wasm-bigint"); - } - isWasmInitialized = true; -}; - -""" - - return res + """ - - -// WASM CODEC - -const nextMultipleOfFour = (value: number) => { - return Math.ceil(value / 4) * 4; -} - -const encodeUint8Array = (inputArray: Uint8Array) => { - const cArrayPointer = wasm.TS_malloc(inputArray.length + 4); - const arrayLengthView = new Uint32Array(wasm.memory.buffer, cArrayPointer, 1); - arrayLengthView[0] = inputArray.length; - const arrayMemoryView = new Uint8Array(wasm.memory.buffer, cArrayPointer + 4, inputArray.length); - arrayMemoryView.set(inputArray); - return cArrayPointer; -} - -const encodeUint32Array = (inputArray: Uint32Array) => { - const cArrayPointer = wasm.TS_malloc((inputArray.length + 1) * 4); - const arrayMemoryView = new Uint32Array(wasm.memory.buffer, cArrayPointer, inputArray.length); - arrayMemoryView.set(inputArray, 1); - arrayMemoryView[0] = inputArray.length; - return cArrayPointer; -} - -const getArrayLength = (arrayPointer: number) => { - const arraySizeViewer = new Uint32Array( - wasm.memory.buffer, // value - arrayPointer, // offset - 1 // one int - ); - return arraySizeViewer[0]; -} -const decodeUint8Array = (arrayPointer: number, free = true) => { - const arraySize = getArrayLength(arrayPointer); - const actualArrayViewer = new Uint8Array( - wasm.memory.buffer, // value - arrayPointer + 4, // offset (ignoring length bytes) - arraySize // uint8 count - ); - // Clone the contents, TODO: In the future we should wrap the Viewer in a class that - // will free the underlying memory when it becomes unreachable instead of copying here. - const actualArray = actualArrayViewer.slice(0, arraySize); - if (free) { - wasm.TS_free(arrayPointer); - } - return actualArray; -} -const decodeUint32Array = (arrayPointer: number, free = true) => { - const arraySize = getArrayLength(arrayPointer); - const actualArrayViewer = new Uint32Array( - wasm.memory.buffer, // value - arrayPointer + 4, // offset (ignoring length bytes) - arraySize // uint32 count - ); - // Clone the contents, TODO: In the future we should wrap the Viewer in a class that - // will free the underlying memory when it becomes unreachable instead of copying here. - const actualArray = actualArrayViewer.slice(0, arraySize); - if (free) { - wasm.TS_free(arrayPointer); - } - return actualArray; -} - -export function encodeString(str: string): number { - const charArray = new TextEncoder().encode(str); - return encodeUint8Array(charArray); -} - -export function decodeString(stringPointer: number, free = true): string { - const arraySize = getArrayLength(stringPointer); - const memoryView = new Uint8Array(wasm.memory.buffer, stringPointer + 4, arraySize); - const result = new TextDecoder("utf-8").decode(memoryView); - - if (free) { - wasm.TS_free(stringPointer); - } - - return result; -} -""" - def init_str(self): return "" + def get_java_arr_len(self, arr_name): + return "bindings.getArrayLength(" + arr_name + ")" + def get_java_arr_elem(self, elem_ty, arr_name, idx): + if elem_ty.c_ty == "uint32_t" or elem_ty.c_ty == "uintptr_t" or elem_ty.c_ty.endswith("Array"): + return "bindings.getU32ArrayElem(" + arr_name + ", " + idx + ")" + elif elem_ty.rust_obj == "LDKu5": + return "bindings.getU8ArrayElem(" + arr_name + ", " + idx + ")" + else: + assert False def constr_hu_array(self, ty_info, arr_len): return "new Array(" + arr_len + ").fill(null)" + def cleanup_converted_native_array(self, ty_info, arr_name): + return "bindings.freeWasmMemory(" + arr_name + ")" + + def primitive_arr_from_hu(self, mapped_ty, fixed_len, arr_name): + inner = arr_name + if fixed_len is not None: + assert mapped_ty.c_ty == "int8_t" + inner = "bindings.check_arr_len(" + arr_name + ", " + fixed_len + ")" + if mapped_ty.c_ty.endswith("Array"): + return ("bindings.encodeUint32Array(" + inner + ")", "") + elif mapped_ty.c_ty == "uint8_t" or mapped_ty.c_ty == "int8_t": + return ("bindings.encodeUint8Array(" + inner + ")", "") + elif mapped_ty.c_ty == "uint32_t": + return ("bindings.encodeUint32Array(" + inner + ")", "") + elif mapped_ty.c_ty == "int64_t": + return ("bindings.encodeUint64Array(" + inner + ")", "") + else: + print(mapped_ty.c_ty) + assert False + + def primitive_arr_to_hu(self, mapped_ty, fixed_len, arr_name, conv_name): + assert mapped_ty.c_ty == "uint8_t" or mapped_ty.c_ty == "int8_t" + return "const " + conv_name + ": Uint8Array = bindings.decodeUint8Array(" + arr_name + ");" def var_decl_statement(self, ty_string, var_name, statement): return "const " + var_name + ": " + ty_string + " = " + statement + def java_arr_ty_str(self, elem_ty_str): + return "number" + def for_n_in_range(self, n, minimum, maximum): return "for (var " + n + " = " + minimum + "; " + n + " < " + maximum + "; " + n + "++) {" def for_n_in_arr(self, n, arr_name, arr_elem_ty): @@ -571,8 +733,9 @@ export function decodeString(stringPointer: number, free = true): string { out_c = out_c + "\t\tcase %d: return %s;\n" % (ord_v, var) ord_v = ord_v + 1 if var_docs is not None: - out_typescript_enum_fields += f"/**\n * {var_docs}\n */\n" - out_typescript_enum_fields += f"{var},\n\t\t\t\t" + var_docs_repld = var_docs.replace("\n", "\n\t") + out_typescript_enum_fields += f"/**\n\t * {var_docs_repld}\n\t */\n" + out_typescript_enum_fields += f"\t{var},\n\t" out_c = out_c + "\t}\n" out_c = out_c + "\tabort();\n" out_c = out_c + "}\n" @@ -588,9 +751,10 @@ export function decodeString(stringPointer: number, free = true): string { out_c = out_c + "}\n" out_typescript = f""" - export enum {struct_name} {{ - {out_typescript_enum_fields} - }} +/* @internal */ +export enum {struct_name} {{ + {out_typescript_enum_fields} +}} """ out_typescript_enum = f"export {{ {struct_name} }} from \"../bindings.mjs\";" self.obj_defined([struct_name], "enums") @@ -602,8 +766,7 @@ export function decodeString(stringPointer: number, free = true): string { return (ty_info.rust_obj + "_from_js(", ")") def native_c_map_trait(self, struct_name, field_var_conversions, flattened_field_var_conversions, field_function_lines, trait_doc_comment): - out_typescript_bindings = "\n\n\n// OUT_TYPESCRIPT_BINDINGS :: MAP_TRAIT :: START\n\n" - + out_typescript_bindings = "" super_instantiator = "" bindings_instantiator = "" pointer_to_adder = "" @@ -645,6 +808,7 @@ export function decodeString(stringPointer: number, free = true): string { for fn_line in field_function_lines: java_method_descriptor = "" if fn_line.fn_name != "free" and fn_line.fn_name != "cloned": + out_java_interface += "\t/**" + fn_line.docs.replace("\n", "\n\t * ") + "\n\t */\n" out_java_interface += "\t" + fn_line.fn_name + "(" out_interface_implementation_overrides += f"\t\t\t{fn_line.fn_name} (" @@ -693,9 +857,11 @@ export function decodeString(stringPointer: number, free = true): string { out_interface_implementation_overrides += "\t\t\t\treturn ret;\n" out_interface_implementation_overrides += f"\t\t\t}},\n" + formatted_trait_docs = trait_doc_comment.replace("\n", "\n * ") out_typescript_human = f""" {self.hu_struct_file_prefix} +/** An implementation of {struct_name.replace("LDK","")} */ export interface {struct_name.replace("LDK", "")}Interface {{ {out_java_interface}}} @@ -703,6 +869,9 @@ class {struct_name}Holder {{ held: {struct_name.replace("LDK", "")}; }} +/** + * {formatted_trait_docs} + */ export class {struct_name.replace("LDK","")} extends CommonBase {{ /* @internal */ public bindings_instance?: bindings.{struct_name}; @@ -713,7 +882,8 @@ export class {struct_name.replace("LDK","")} extends CommonBase {{ this.bindings_instance = null; }} - static new_impl(arg: {struct_name.replace("LDK", "")}Interface{impl_constructor_arguments}): {struct_name.replace("LDK", "")} {{ + /** Creates a new instance of {struct_name.replace("LDK","")} from a given implementation */ + public static new_impl(arg: {struct_name.replace("LDK", "")}Interface{impl_constructor_arguments}): {struct_name.replace("LDK", "")} {{ const impl_holder: {struct_name}Holder = new {struct_name}Holder(); let structImplementation = {{ {out_interface_implementation_overrides} }} as bindings.{struct_name}; @@ -723,10 +893,11 @@ export class {struct_name.replace("LDK","")} extends CommonBase {{ impl_holder.held.bindings_instance = structImplementation; {pointer_to_adder} return impl_holder.held; }} + """ self.obj_defined([struct_name.replace("LDK", ""), struct_name.replace("LDK", "") + "Interface"], "structs") - out_typescript_bindings += "export interface " + struct_name + " {\n" + out_typescript_bindings += "/* @internal */\nexport interface " + struct_name + " {\n" java_meths = [] for fn_line in field_function_lines: if fn_line.fn_name != "free" and fn_line.fn_name != "cloned": @@ -741,7 +912,7 @@ export class {struct_name.replace("LDK","")} extends CommonBase {{ out_typescript_bindings += "}\n\n" - out_typescript_bindings += f"export function {struct_name}_new(impl: {struct_name}" + out_typescript_bindings += f"/* @internal */\nexport function {struct_name}_new(impl: {struct_name}" for var in flattened_field_var_conversions: if isinstance(var, ConvInfo): out_typescript_bindings += f", {var.arg_name}: {var.java_ty}" @@ -761,8 +932,6 @@ export class {struct_name.replace("LDK","")} extends CommonBase {{ }} """ - out_typescript_bindings += '\n// OUT_TYPESCRIPT_BINDINGS :: MAP_TRAIT :: END\n\n\n' - # Now that we've written out our java code (and created java_meths), generate C out_c = "typedef struct " + struct_name + "_JCalls {\n" out_c += "\tatomic_size_t refcnt;\n" @@ -925,7 +1094,7 @@ export class {struct_name.replace("LDK","")} extends CommonBase {{ out_java_enum += (self.hu_struct_file_prefix) - java_hu_class = "" + java_hu_class = "/**\n * " + enum_doc_comment.replace("\n", "\n * ") + "\n */\n" java_hu_class += "export class " + java_hu_type + " extends CommonBase {\n" java_hu_class += "\tprotected constructor(_dummy: object, ptr: number) { super(ptr, bindings." + bindings_type + "_free); }\n" java_hu_class += "\t/* @internal */\n" @@ -937,17 +1106,20 @@ export class {struct_name.replace("LDK","")} extends CommonBase {{ java_hu_class += "\t\tswitch (raw_ty) {\n" java_hu_subclasses = "" - out_java += "export class " + struct_name + " {\n" + out_java += "/* @internal */\nexport class " + struct_name + " {\n" out_java += "\tprotected constructor() {}\n" var_idx = 0 for var in variant_list: - java_hu_subclasses = java_hu_subclasses + "export class " + java_hu_type + "_" + var.var_name + " extends " + java_hu_type + " {\n" + java_hu_subclasses += "/** A " + java_hu_type + " of type " + var.var_name + " */\n" + java_hu_subclasses += "export class " + java_hu_type + "_" + var.var_name + " extends " + java_hu_type + " {\n" java_hu_class += f"\t\t\tcase {var_idx}: " java_hu_class += "return new " + java_hu_type + "_" + var.var_name + "(ptr);\n" 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): - java_hu_subclasses = java_hu_subclasses + "\tpublic " + field_ty.arg_name + f": {field_ty.java_hu_ty};\n" + if field_docs is not None: + java_hu_subclasses += "\t/**\n\t * " + field_docs.replace("\n", "\n\t * ") + "\n\t */\n" + java_hu_subclasses += "\tpublic " + field_ty.arg_name + f": {field_ty.java_hu_ty};\n" if field_ty.to_hu_conv is not None: hu_conv_body += f"\t\tconst {field_ty.arg_name}: {field_ty.java_ty} = bindings.{struct_name}_{var.var_name}_get_{field_ty.arg_name}(ptr);\n" hu_conv_body += f"\t\t" + field_ty.to_hu_conv.replace("\n", "\n\t\t\t") + "\n" @@ -999,7 +1171,11 @@ export class {struct_name.replace("LDK","")} extends CommonBase {{ out_opaque_struct_human = f"{self.hu_struct_file_prefix}" if struct_name.startswith("LDKLocked"): out_opaque_struct_human += "/** XXX: DO NOT USE THIS - it remains locked until the GC runs (if that ever happens */" + formatted_doc_comment = struct_doc_comment.replace("\n", "\n * ") out_opaque_struct_human += f""" +/** + * {formatted_doc_comment} + */ export class {hu_name} extends CommonBase {implementations}{{ /* @internal */ public constructor(_dummy: object, ptr: number) {{ @@ -1073,15 +1249,12 @@ export class {human_ty} extends CommonBase {{ def fn_call_body(self, method_name, return_c_ty, return_java_ty, method_argument_string, native_call_argument_string): has_return_value = return_c_ty != 'void' - needs_decoding = return_c_ty in self.wasm_decoding_map return_statement = 'return nativeResponseValue;' if not has_return_value: return_statement = '// debug statements here' - elif needs_decoding: - converter = self.wasm_decoding_map[return_c_ty] - return_statement = f"return {converter}(nativeResponseValue);" - return f"""export function {method_name}({method_argument_string}): {return_java_ty} {{ + return f"""/* @internal */ +export function {method_name}({method_argument_string}): {return_java_ty} {{ if(!isWasmInitialized) {{ throw new Error("initializeWasm() must be awaited first!"); }} @@ -1112,16 +1285,14 @@ export class {human_ty} extends CommonBase {{ out_c += (", ") if arg_conv_info.c_ty != "void": out_c += (arg_conv_info.c_ty + " " + arg_conv_info.arg_name) - needs_encoding = arg_conv_info.c_ty in self.wasm_encoding_map - native_argument = arg_conv_info.arg_name - if needs_encoding: - converter = self.wasm_encoding_map[arg_conv_info.c_ty] - native_argument = f"{converter}({arg_conv_info.arg_name})" method_argument_string += f"{arg_conv_info.arg_name}: {arg_conv_info.java_ty}" - native_call_argument_string += native_argument + native_call_argument_string += arg_conv_info.arg_name out_java = self.fn_call_body(method_name, return_type_info.c_ty, return_type_info.java_ty, method_argument_string, native_call_argument_string) out_java_struct = "" + if doc_comment is not None: + out_java_struct = "\t/**\n\t * " + doc_comment.replace("\n", "\n\t * ") + "\n\t */\n" + if not args_known: out_java_struct += ("\t// Skipped " + method_name + "\n") else: