From 79d81af1dcf68ebcf96e58dfe89ab04291b9a009 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 30 Jun 2022 04:19:13 +0000 Subject: [PATCH] [TS] Split C -> JS function calls based on u32/u64 parameters/return We were calling all C -> JS functions with u32 parameters and return types. This is fine for all of our pointers, as well as any smaller types - u32 always gets mapped to a JavaScript `number`, so its all consistent. However, for u64 parameters/returns, we map the values to JavaScript `bigint`s, which are not compatible with `numbers`, and the correct type is checked at the FFI when returning or when users ultimately try to use a passed `bigint` as if it were a `number`. Thus, we have to split the `js_invoke_function` family by the parameters and return values, using `u` for u32s and `b` for u64s to do so. --- ts/js-wasm.h | 54 ++++++++++++++++++++++++------------------- typescript_strings.py | 32 +++++++++++++++++-------- 2 files changed, 53 insertions(+), 33 deletions(-) diff --git a/ts/js-wasm.h b/ts/js-wasm.h index abcb5b30..94466cf4 100644 --- a/ts/js-wasm.h +++ b/ts/js-wasm.h @@ -4,40 +4,46 @@ extern size_t strlen(const char *s); +// We only support two call types - u32 and u64. u32 is mapped to a JavaScript +// "number" whereas u64 is mapped to a JavaScript "bigint". Ultimately it all +// calls through to the same function, but at the FFI boundary itself we have +// to use proper types. + typedef uint32_t JSValue; -extern JSValue js_invoke_function(JSValue,JSValue,JSValue,JSValue,JSValue,JSValue,JSValue,JSValue,JSValue,JSValue,JSValue,JSValue) __attribute__((import_name("js_invoke_function"))); +extern uint64_t js_invoke_function_u(JSValue,JSValue,JSValue,JSValue,JSValue,JSValue,JSValue,JSValue,JSValue,JSValue,JSValue,JSValue) __attribute__((import_name("js_invoke_function_u"))); +extern uint64_t js_invoke_function_b(JSValue,JSValue,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t,uint64_t) __attribute__((import_name("js_invoke_function_b"))); -static inline JSValue js_invoke_function_0(JSValue obj, JSValue fn){ - return js_invoke_function(obj,fn,0,0,0,0,0,0,0,0,0,0); -} -static inline JSValue js_invoke_function_1(JSValue obj, JSValue fn, JSValue a){ - return js_invoke_function(obj,fn,a,0,0,0,0,0,0,0,0,0); +static inline JSValue js_invoke_function_u_(JSValue obj, JSValue fn){ + return js_invoke_function_u(obj,fn,0,0,0,0,0,0,0,0,0,0); } -static inline JSValue js_invoke_function_2(JSValue obj, JSValue fn, JSValue a, JSValue b){ - return js_invoke_function(obj,fn,a,b,0,0,0,0,0,0,0,0); +static inline JSValue js_invoke_function_u_u(JSValue obj, JSValue fn, JSValue a){ + return js_invoke_function_u(obj,fn,a,0,0,0,0,0,0,0,0,0); } -static inline JSValue js_invoke_function_3(JSValue obj, JSValue fn, JSValue a, JSValue b, JSValue c){ - return js_invoke_function(obj,fn,a,b,c,0,0,0,0,0,0,0); +static inline JSValue js_invoke_function_u_uu(JSValue obj, JSValue fn, JSValue a, JSValue b){ + return js_invoke_function_u(obj,fn,a,b,0,0,0,0,0,0,0,0); } -static inline JSValue js_invoke_function_4(JSValue obj, JSValue fn, JSValue a, JSValue b, JSValue c, JSValue d){ - return js_invoke_function(obj,fn,a,b,c,d,0,0,0,0,0,0); +static inline JSValue js_invoke_function_u_uuu(JSValue obj, JSValue fn, JSValue a, JSValue b, JSValue c){ + return js_invoke_function_u(obj,fn,a,b,c,0,0,0,0,0,0,0); } -static inline JSValue js_invoke_function_5(JSValue obj, JSValue fn, JSValue a, JSValue b, JSValue c, JSValue d, JSValue e){ - return js_invoke_function(obj,fn,a,b,c,d,e,0,0,0,0,0); +static inline JSValue js_invoke_function_u_uuuu(JSValue obj, JSValue fn, JSValue a, JSValue b, JSValue c, JSValue d){ + return js_invoke_function_u(obj,fn,a,b,c,d,0,0,0,0,0,0); } -static inline JSValue js_invoke_function_6(JSValue obj, JSValue fn, JSValue a, JSValue b, JSValue c, JSValue d, JSValue e, JSValue f){ - return js_invoke_function(obj,fn,a,b,c,d,e,f,0,0,0,0); +static inline JSValue js_invoke_function_u_uuuuu(JSValue obj, JSValue fn, JSValue a, JSValue b, JSValue c, JSValue d, JSValue e){ + return js_invoke_function_u(obj,fn,a,b,c,d,e,0,0,0,0,0); } -static inline JSValue js_invoke_function_7(JSValue obj, JSValue fn, JSValue a, JSValue b, JSValue c, JSValue d, JSValue e, JSValue f, JSValue g){ - return js_invoke_function(obj,fn,a,b,c,d,e,f,g,0,0,0); + +static inline uint64_t js_invoke_function_b_(JSValue obj, JSValue fn){ + return js_invoke_function_u(obj,fn,0,0,0,0,0,0,0,0,0,0); } -static inline JSValue js_invoke_function_8(JSValue obj, JSValue fn, JSValue a, JSValue b, JSValue c, JSValue d, JSValue e, JSValue f, JSValue g, JSValue h){ - return js_invoke_function(obj,fn,a,b,c,d,e,f,g,h,0,0); + +static inline uint64_t js_invoke_function_b_uuuu(JSValue obj, JSValue fn, JSValue a, JSValue b, JSValue c, JSValue d){ + return js_invoke_function_u(obj,fn,a,b,c,d,0,0,0,0,0,0); } -static inline JSValue js_invoke_function_9(JSValue obj, JSValue fn, JSValue a, JSValue b, JSValue c, JSValue d, JSValue e, JSValue f, JSValue g, JSValue h, JSValue i){ - return js_invoke_function(obj,fn,a,b,c,d,e,f,g,h,i,0); +static inline JSValue js_invoke_function_u_b(JSValue obj, JSValue fn, uint64_t a){ + return js_invoke_function_b(obj,fn,a,0,0,0,0,0,0,0,0,0); } -static inline JSValue js_invoke_function_10(JSValue obj, JSValue fn, JSValue a, JSValue b, JSValue c, JSValue d, JSValue e, JSValue f, JSValue g, JSValue h, JSValue i, JSValue j){ - return js_invoke_function(obj,fn,a,b,c,d,e,f,g,h,i,j); +static inline JSValue js_invoke_function_u_bb(JSValue obj, JSValue fn, uint64_t a, uint64_t b){ + return js_invoke_function_b(obj,fn,a,b,0,0,0,0,0,0,0,0); } + #endif diff --git a/typescript_strings.py b/typescript_strings.py index 9e4cf192..4e458f1b 100644 --- a/typescript_strings.py +++ b/typescript_strings.py @@ -124,7 +124,8 @@ async function finishInitializeWasm(wasmInstance: WebAssembly.Instance) { /* @internal */ export async function initializeWasmFromUint8Array(wasmBinary: Uint8Array) { - imports.env["js_invoke_function"] = js_invoke; + imports.env["js_invoke_function_u"] = js_invoke; + imports.env["js_invoke_function_b"] = js_invoke; const { instance: wasmInstance } = await WebAssembly.instantiate(wasmBinary, imports); await finishInitializeWasm(wasmInstance); } @@ -132,7 +133,8 @@ export async function initializeWasmFromUint8Array(wasmBinary: Uint8Array) { /* @internal */ export async function initializeWasmFetch(uri: string) { const stream = fetch(uri); - imports.env["js_invoke_function"] = js_invoke; + imports.env["js_invoke_function_u"] = js_invoke; + imports.env["js_invoke_function_b"] = js_invoke; const { instance: wasmInstance } = await WebAssembly.instantiateStreaming(stream, imports); await finishInitializeWasm(wasmInstance); }""" @@ -1001,17 +1003,27 @@ export class {struct_name.replace("LDK","")} extends CommonBase {{ out_c = out_c + arg_info.arg_name out_c = out_c + arg_info.ret_conv[1].replace('\n', '\n\t') + "\n" + fn_suffix = "" + if fn_line.ret_ty_info.c_ty == "uint64_t" or fn_line.ret_ty_info.c_ty == "int64_t": + fn_suffix += "b_" + else: + fn_suffix += "u_" + for arg in fn_line.args_ty: + if arg_info.c_ty == "uint64_t" or arg_info.c_ty == "int64_t": + fn_suffix += "b" + else: + fn_suffix += "u" if fn_line.ret_ty_info.c_ty.endswith("Array"): out_c += "\t" + fn_line.ret_ty_info.c_ty + " ret = (" + fn_line.ret_ty_info.c_ty + ")" - out_c += "js_invoke_function_" + str(len(fn_line.args_ty)) + "(j_calls->instance_ptr, " + str(self.function_ptr_counter) + out_c += "js_invoke_function_" + fn_suffix + "(j_calls->instance_ptr, " + str(self.function_ptr_counter) elif fn_line.ret_ty_info.java_ty == "void": - out_c = out_c + "\tjs_invoke_function_" + str(len(fn_line.args_ty)) + "(j_calls->instance_ptr, " + str(self.function_ptr_counter) + out_c = out_c + "\tjs_invoke_function_" + fn_suffix + "(j_calls->instance_ptr, " + str(self.function_ptr_counter) elif fn_line.ret_ty_info.java_hu_ty == "string": - out_c = out_c + "\tjstring ret = (jstring)js_invoke_function_" + str(len(fn_line.args_ty)) + "(j_calls->instance_ptr, " + str(self.function_ptr_counter) + out_c += "\tjstring ret = (jstring)js_invoke_function_" + fn_suffix + "(j_calls->instance_ptr, " + str(self.function_ptr_counter) elif not fn_line.ret_ty_info.passed_as_ptr: - out_c = out_c + "\treturn js_invoke_function_" + str(len(fn_line.args_ty)) + "(j_calls->instance_ptr, " + str(self.function_ptr_counter) + out_c += "\treturn js_invoke_function_" + fn_suffix + "(j_calls->instance_ptr, " + str(self.function_ptr_counter) else: - out_c = out_c + "\tuint32_t ret = js_invoke_function_" + str(len(fn_line.args_ty)) + "(j_calls->instance_ptr, " + str(self.function_ptr_counter) + out_c += "\tuint32_t ret = js_invoke_function_" + fn_suffix + "(j_calls->instance_ptr, " + str(self.function_ptr_counter) self.function_ptrs[self.function_ptr_counter] = (struct_name, fn_line.fn_name) self.function_ptr_counter += 1 @@ -1450,7 +1462,7 @@ export function {method_name}({method_argument_string}): {return_java_ty} {{ with open(self.outdir + "/bindings.mts", "a") as bindings: bindings.write(""" -js_invoke = function(obj_ptr: number, fn_id: number, arg1: number, arg2: number, arg3: number, arg4: number, arg5: number, arg6: number, arg7: number, arg8: number, arg9: number, arg10: number) { +js_invoke = function(obj_ptr: number, fn_id: number, arg1: bigint|number, arg2: bigint|number, arg3: bigint|number, arg4: bigint|number, arg5: bigint|number, arg6: bigint|number, arg7: bigint|number, arg8: bigint|number, arg9: bigint|number, arg10: bigint|number) { const weak: WeakRef = js_objs[obj_ptr]; if (weak == null || weak == undefined) { console.error("Got function call on unknown/free'd JS object!"); @@ -1475,5 +1487,7 @@ js_invoke = function(obj_ptr: number, fn_id: number, arg1: number, arg2: number, console.error("Got function call on incorrect JS object!"); throw new Error("Got function call on incorrect JS object!"); } - return fn.value.bind(obj)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); + const ret = fn.value.bind(obj)(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); + if (ret === undefined || ret === null) return BigInt(0); + return BigInt(ret); }""") -- 2.39.5