Mark function arguments and return values Nullable if relevant
authorMatt Corallo <git@bluematt.me>
Fri, 6 Aug 2021 17:09:55 +0000 (17:09 +0000)
committerMatt Corallo <git@bluematt.me>
Mon, 9 Aug 2021 05:53:31 +0000 (05:53 +0000)
Fixes #35.

bindingstypes.py
genbindings.py
java_strings.py

index 2ca6c0de11712ed570eca663a8e9b111e22720ee..edb19565140c0c435892615810e59986414b6778 100644 (file)
@@ -65,6 +65,8 @@ class ConvInfo:
         self.to_hu_conv = to_hu_conv
         self.to_hu_conv_name = to_hu_conv_name
         self.from_hu_conv = from_hu_conv
+        # This is set based on docstrings in various contexts:
+        self.nullable = False
 
 class TraitMethInfo:
     def __init__(self, fn_name, self_is_const, ret_ty_info, args_ty, docs):
index 5e8071ec00b31e1c93a9d6a05ef69d9dbc043e37..e8cde68359c1f75597d1f6f487978c9a89ba2018 100755 (executable)
@@ -70,6 +70,21 @@ def camel_to_snake(s):
             lastund = True
     return (ret + lastchar.lower()).strip("_")
 
+def doc_to_params_ret_nullable(doc):
+    if doc is None:
+        return (set(), False)
+    params = set()
+    ret_null = False
+    for line in doc.splitlines():
+        if "may be NULL or all-0s to represent None" not in line:
+            continue
+        if "Note that the return value" in line:
+            ret_null = True
+        elif "Note that " in line:
+            param = line.split("Note that ")[1].split(" ")[0]
+            params.add(param)
+    return (params, ret_null)
+
 unitary_enums = set()
 complex_enums = set()
 opaque_structs = set()
@@ -418,6 +433,10 @@ with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java:
 
         return_type_info = type_mapping_generator.map_type(method_return_type.strip() + " ret", True, ret_arr_len, False, False)
 
+        (params_nullable, ret_nullable) = doc_to_params_ret_nullable(doc_comment)
+        if ret_nullable:
+            return_type_info.nullable = True
+
         argument_types = []
         default_constructor_args = {}
         takes_self = False
@@ -430,7 +449,10 @@ with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java:
                 takes_self = True
                 if argument_conversion_info.ty_info.is_ptr:
                     takes_self_ptr = True
+            elif argument_conversion_info.arg_name in params_nullable:
+                argument_conversion_info.nullable = True
             if argument_conversion_info.arg_conv is not None and "Warning" in argument_conversion_info.arg_conv:
+                assert not argument_conversion_info.arg_name in params_nullable
                 if argument_conversion_info.rust_obj in constructor_fns:
                     assert not is_free
                     for explode_arg in constructor_fns[argument_conversion_info.rust_obj].split(','):
@@ -576,12 +598,20 @@ with open(sys.argv[1]) as in_h, open(sys.argv[2], "w") as out_java:
                 else:
                     ret_ty_info = type_mapping_generator.map_type(fn_line.group(2).strip() + " ret", True, None, False, False)
                     is_const = fn_line.group(4) is not None
+                    (nullable_params, ret_nullable) = doc_to_params_ret_nullable(fn_docs)
+                    if ret_nullable:
+                        assert False # This isn't yet handled on the Java side
+                        ret_ty_info.nullable = True
 
                     arg_tys = []
                     for idx, arg in enumerate(fn_line.group(5).split(',')):
                         if arg == "":
                             continue
                         arg_conv_info = type_mapping_generator.map_type(arg, True, None, False, False)
+                        if arg_conv_info.arg_name in nullable_params:
+                            # Types that are actually null instead of all-0s aren't yet handled on the Java side:
+                            assert arg_conv_info.rust_obj == "LDKPublicKey"
+                            arg_conv_info.nullable = True
                         arg_tys.append(arg_conv_info)
                     field_fns.append(TraitMethInfo(fn_line.group(3), is_const, ret_ty_info, arg_tys, fn_docs))
 
index d4931cd912038295c89ab63ce11f0ffdbe9e35e7..82efd47b72be17330f3a27a09ee0c16007b9273c 100644 (file)
@@ -96,6 +96,7 @@ public class bindings {
         self.util_fn_pfx = """package org.ldk.structs;
 import org.ldk.impl.bindings;
 import java.util.Arrays;
+import javax.annotation.Nullable;
 import org.ldk.enums.*;
 
 public class UtilMethods {
@@ -473,6 +474,7 @@ import org.ldk.impl.bindings;
 import org.ldk.enums.*;
 import org.ldk.util.*;
 import java.util.Arrays;
+import javax.annotation.Nullable;
 
 """
         self.c_fn_ty_pfx = "JNIEXPORT "
@@ -1137,6 +1139,8 @@ import java.util.Arrays;
             meth_n = method_name[len(struct_meth) + 1 if len(struct_meth) != 0 else 0:].strip("_")
             if doc_comment is not None:
                 out_java_struct += "\t/**\n\t * " + doc_comment.replace("\n", "\n\t * ") + "\n\t */\n"
+            if return_type_info.nullable:
+                out_java_struct += "\t@Nullable\n"
             if not takes_self:
                 if meth_n == "new":
                     out_java_struct += "\tpublic static " + return_type_info.java_hu_ty + " of("
@@ -1154,12 +1158,15 @@ import java.util.Arrays;
                     continue
                 if arg.java_ty != "void":
                     if arg.arg_name in default_constructor_args:
+                        assert not arg.nullable
                         for explode_idx, explode_arg in enumerate(default_constructor_args[arg.arg_name]):
                             if explode_idx != 0:
                                 out_java_struct += (", ")
                             out_java_struct += (
                                 explode_arg.java_hu_ty + " " + arg.arg_name + "_" + explode_arg.arg_name)
                     else:
+                        if arg.nullable:
+                            out_java_struct += "@Nullable "
                         out_java_struct += (arg.java_hu_ty + " " + arg.arg_name)
         out_java += (");\n")
         out_c += (") {\n")