[TS] Split C -> JS function calls based on u32/u64 parameters/return
authorMatt Corallo <git@bluematt.me>
Thu, 30 Jun 2022 04:19:13 +0000 (04:19 +0000)
committerMatt Corallo <git@bluematt.me>
Thu, 30 Jun 2022 19:03:29 +0000 (19:03 +0000)
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
typescript_strings.py

index abcb5b301babc34d0e325409fd762e350170b0aa..94466cf4562cb5ae9cd7cdeacec178991cea238c 100644 (file)
@@ -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
index 9e4cf192a1aa76abf604c1b3123d7265b9840e0c..4e458f1b5962e9f89bad35eba56f5a3111195c38 100644 (file)
@@ -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<object> = 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);
 }""")