Skip to content

Commit 55f3d30

Browse files
committed
fix reference prefix for mutable pointer-based constructors
1 parent 5672f90 commit 55f3d30

File tree

9 files changed

+322
-5
lines changed

9 files changed

+322
-5
lines changed

src/generators/opaque_struct_generator.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,21 @@ def generate_opaque_struct(self, struct_name, struct_details, all_type_details={
4242
constructor_argument_conversion_method = f'let converted_{argument_name} = Bindings.new_{current_argument_details.rust_obj}(array: {argument_name})'
4343
constructor_argument_prep += '\n\t\t'+constructor_argument_conversion_method
4444
passed_argument_name = f'converted_{argument_name}'
45+
elif current_argument_details.is_ptr:
46+
passed_argument_name = argument_name + 'Pointer'
47+
requires_mutability = not current_argument_details.is_const
48+
mutability_infix = ''
49+
reference_prefix = ''
50+
if requires_mutability:
51+
# argument_name = '&' + argument_name
52+
reference_prefix = '&'
53+
mutability_infix = 'Mutable'
54+
current_prep = f'''
55+
\n\t let {passed_argument_name} = withUnsafe{mutability_infix}Pointer(to: {reference_prefix}{argument_name}.cOpaqueStruct!) {{ (pointer: Unsafe{mutability_infix}Pointer<{current_argument_details.rust_obj}>) in
56+
\n\t\t pointer
57+
\n\t }}
58+
'''
59+
constructor_argument_prep += current_prep
4560
elif current_argument_details.rust_obj == 'LDK' + current_argument_details.swift_type:
4661
passed_argument_name += '.cOpaqueStruct!'
4762

@@ -97,8 +112,9 @@ def generate_opaque_struct(self, struct_name, struct_details, all_type_details={
97112
f'return {return_type_wrapper_prefix}OpaqueStructType_methodName(native_arguments){return_type_wrapper_suffix}')
98113
elif current_return_type.rust_obj == 'LDK' + current_return_type.swift_type and not is_clone_method:
99114
return_type_wrapper_prefix = f'{current_method_details["return_type"].swift_type}(pointer: '
100-
# return_type_wrapper_suffix = '.pointee)'
101115
return_type_wrapper_suffix = ')'
116+
if current_return_type.is_const:
117+
return_type_wrapper_suffix = '.pointee)'
102118
current_replacement = current_replacement.replace(
103119
'return OpaqueStructType_methodName(native_arguments)',
104120
f'return {return_type_wrapper_prefix}OpaqueStructType_methodName(native_arguments){return_type_wrapper_suffix}')
@@ -168,6 +184,8 @@ def generate_opaque_struct(self, struct_name, struct_details, all_type_details={
168184
elif current_argument_details.rust_obj is not None and current_argument_details.rust_obj.startswith(
169185
'LDK') and current_argument_details.swift_type.startswith('['):
170186
native_arguments.append(f'Bindings.new_{current_argument_details.rust_obj}(array: {passed_argument_name})')
187+
elif current_argument_details.rust_obj is None and current_argument_details.arr_len is not None and current_argument_details.arr_len.isnumeric():
188+
native_arguments.append(f'Bindings.array_to_tuple{current_argument_details.arr_len}(array: {passed_argument_name})')
171189
else:
172190
native_arguments.append(f'{passed_argument_name}')
173191

src/generators/option_generator.py

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
import re
2+
import os
3+
4+
from config import Config
5+
6+
7+
class OptionGenerator:
8+
9+
def __init__(self) -> None:
10+
super().__init__()
11+
template_path = f'{os.path.dirname(__file__)}/../../templates/OptionTemplate.swift'
12+
with open(template_path, 'r') as template_handle:
13+
template = template_handle.read()
14+
self.template = template
15+
16+
def generate_option(self, struct_name, struct_details, all_type_details={}):
17+
# method_names = ['openChannel', 'closeChannel']
18+
# native_method_names = ['ChannelHandler_openChannel', 'ChannelHandler_closeChannel']
19+
20+
swift_struct_name = struct_name[3:]
21+
if swift_struct_name.startswith('COption'):
22+
swift_struct_name = struct_name[4:]
23+
24+
mutating_output_file_contents = self.template
25+
26+
# CONSTRUCTOR START
27+
if struct_details.constructor_method is not None:
28+
# fill constructor details
29+
constructor_details = struct_details.constructor_method
30+
constructor_native_name = constructor_details['name']['native']
31+
swift_arguments = []
32+
native_arguments = []
33+
constructor_argument_prep = ''
34+
for current_argument_details in constructor_details['argument_types']:
35+
argument_name = current_argument_details.var_name
36+
passed_argument_name = argument_name
37+
constructor_argument_conversion_method = None
38+
39+
if current_argument_details.rust_obj is not None and current_argument_details.rust_obj.startswith(
40+
'LDK') and current_argument_details.swift_type.startswith('['):
41+
constructor_argument_conversion_method = f'let converted_{argument_name} = Bindings.new_{current_argument_details.rust_obj}(array: {argument_name})'
42+
constructor_argument_prep += constructor_argument_conversion_method
43+
passed_argument_name = f'converted_{argument_name}'
44+
elif current_argument_details.rust_obj == 'LDK' + current_argument_details.swift_type:
45+
passed_argument_name += '.cOpaqueStruct!'
46+
47+
swift_arguments.append(f'{argument_name}: {current_argument_details.swift_type}')
48+
native_arguments.append(f'{passed_argument_name}')
49+
50+
mutating_output_file_contents = mutating_output_file_contents.replace('swift_constructor_arguments',
51+
', '.join(swift_arguments))
52+
mutating_output_file_contents = mutating_output_file_contents.replace('/* NATIVE_CONSTRUCTOR_PREP */',
53+
constructor_argument_prep)
54+
mutating_output_file_contents = mutating_output_file_contents.replace('native_constructor_arguments',
55+
', '.join(native_arguments))
56+
mutating_output_file_contents = mutating_output_file_contents.replace(
57+
'self.cOpaqueStruct = OptionType(',
58+
f'self.cOpaqueStruct = {constructor_native_name}(')
59+
else:
60+
# remove the default constructor template
61+
constructor_template_regex = re.compile(
62+
"(\/\* DEFAULT_CONSTRUCTOR_START \*\/\n)(.*)(\n[\t ]*\/\* DEFAULT_CONSTRUCTOR_END \*\/)",
63+
flags=re.MULTILINE | re.DOTALL)
64+
mutating_output_file_contents = constructor_template_regex.sub('', mutating_output_file_contents)
65+
66+
# REGULAR METHODS START
67+
68+
method_template_regex = re.compile(
69+
"(\/\* OPTION_METHODS_START \*\/\n)(.*)(\n[\t ]*\/\* OPTION_METHODS_END \*\/)",
70+
flags=re.MULTILINE | re.DOTALL)
71+
method_template = method_template_regex.search(mutating_output_file_contents).group(2)
72+
73+
method_prefix = swift_struct_name + '_'
74+
struct_methods = ''
75+
76+
# fill templates
77+
for current_method_details in struct_details.methods:
78+
current_native_method_name = current_method_details['name']['native']
79+
current_method_name = current_method_details['name']['swift']
80+
current_return_type = current_method_details['return_type'].swift_type
81+
# current_rust_return_type = current_method_details['return_type'].rust_obj
82+
83+
# if current_rust_return_type in all_type_details and all_type_details[current_rust_return_type].type.name == 'UNITARY_ENUM':
84+
# current_return_type = current_rust_return_type
85+
current_method_name = current_native_method_name[len(method_prefix):]
86+
87+
current_replacement = method_template
88+
is_clone_method = current_method_details['is_clone']
89+
90+
if current_method_details['return_type'].rust_obj is not None and current_method_details[
91+
'return_type'].rust_obj.startswith('LDK') and current_method_details[
92+
'return_type'].swift_type.startswith('['):
93+
return_type_wrapper_prefix = f'Bindings.{current_method_details["return_type"].rust_obj}_to_array(nativeType: '
94+
return_type_wrapper_suffix = ')'
95+
current_replacement = current_replacement.replace(
96+
'return OptionType_methodName(native_arguments)',
97+
f'return {return_type_wrapper_prefix}OptionType_methodName(native_arguments){return_type_wrapper_suffix}')
98+
elif current_method_details['return_type'].rust_obj == 'LDK' + current_method_details[
99+
'return_type'].swift_type and not is_clone_method:
100+
return_type_wrapper_prefix = f'{current_method_details["return_type"].swift_type}(pointer: '
101+
return_type_wrapper_suffix = ')'
102+
current_replacement = current_replacement.replace(
103+
'return OptionType_methodName(native_arguments)',
104+
f'return {return_type_wrapper_prefix}OptionType_methodName(native_arguments){return_type_wrapper_suffix}')
105+
106+
current_replacement = current_replacement.replace('func methodName(', f'func {current_method_name}(')
107+
108+
if is_clone_method:
109+
current_replacement = current_replacement.replace('OptionType_methodName(',
110+
f'{swift_struct_name}(pointer: {current_native_method_name}(')
111+
else:
112+
current_replacement = current_replacement.replace('OptionType_methodName(',
113+
f'{current_native_method_name}(')
114+
# replace arguments
115+
swift_arguments = []
116+
native_arguments = []
117+
native_call_prep = ''
118+
for current_argument_details in current_method_details['argument_types']:
119+
pass_instance = False
120+
argument_name = current_argument_details.var_name
121+
passed_argument_name = argument_name
122+
if argument_name == 'this_ptr':
123+
pass_instance = True
124+
125+
if current_argument_details.is_ptr:
126+
passed_argument_name = argument_name + 'Pointer'
127+
requires_mutability = not current_argument_details.is_const
128+
129+
mutability_infix = ''
130+
131+
if pass_instance:
132+
argument_name = 'self'
133+
if requires_mutability:
134+
argument_name = '&' + argument_name
135+
mutability_infix = 'Mutable'
136+
# let managerPointer = withUnsafePointer(to: self.cChannelManager!) { (pointer: UnsafePointer<LDKChannelManager>) in
137+
# pointer
138+
# }
139+
# the \n\t will add a bunch of extra lines, but this file will be easier to read
140+
current_prep = f'''
141+
\n\t let {passed_argument_name} = withUnsafe{mutability_infix}Pointer(to: {argument_name}.cOpaqueStruct!) {{ (pointer: Unsafe{mutability_infix}Pointer<{current_argument_details.rust_obj}>) in
142+
\n\t\t pointer
143+
\n\t }}
144+
'''
145+
native_call_prep += current_prep
146+
147+
if not pass_instance:
148+
swift_arguments.append(f'{argument_name}: {current_argument_details.swift_type}')
149+
150+
# native_arguments.append(f'{passed_argument_name}')
151+
if current_argument_details.rust_obj == 'LDK' + current_argument_details.swift_type and not current_argument_details.is_ptr:
152+
native_arguments.append(f'{passed_argument_name}.cOpaqueStruct!')
153+
else:
154+
native_arguments.append(f'{passed_argument_name}')
155+
156+
current_replacement = current_replacement.replace('swift_arguments', ', '.join(swift_arguments))
157+
if is_clone_method:
158+
# add closing parenthesis that could not be added further up in the clone initializer
159+
current_replacement = current_replacement.replace('native_arguments', ', '.join(native_arguments) + ')')
160+
else:
161+
current_replacement = current_replacement.replace('native_arguments', ', '.join(native_arguments))
162+
current_replacement = current_replacement.replace('/* NATIVE_CALL_PREP */', native_call_prep)
163+
current_replacement = current_replacement.replace('-> Void {', f'-> {current_return_type} {{')
164+
165+
struct_methods += '\n' + current_replacement + '\n'
166+
167+
# DESTRUCTOR START
168+
if struct_details.free_method is not None:
169+
# fill constructor details
170+
free_method_details = struct_details.free_method
171+
free_native_name = free_method_details['name']['native']
172+
native_call_prep = ''
173+
native_arguments = []
174+
for current_argument_details in free_method_details['argument_types']:
175+
pass_instance = False
176+
argument_name = current_argument_details.var_name
177+
passed_argument_name = argument_name
178+
if argument_name.startswith('this_'):
179+
pass_instance = True
180+
181+
if current_argument_details.is_ptr:
182+
passed_argument_name = argument_name + 'Pointer'
183+
requires_mutability = not current_argument_details.is_const
184+
185+
mutability_infix = ''
186+
187+
if pass_instance:
188+
argument_name = 'self'
189+
if requires_mutability:
190+
argument_name = '&' + argument_name
191+
mutability_infix = 'Mutable'
192+
193+
current_prep = f'''
194+
\n\t let {passed_argument_name} = withUnsafe{mutability_infix}Pointer(to: {argument_name}.cOpaqueStruct!) {{ (pointer: Unsafe{mutability_infix}Pointer<{current_argument_details.rust_obj}>) in
195+
\n\t\t pointer
196+
\n\t }}
197+
'''
198+
native_call_prep += current_prep
199+
elif pass_instance:
200+
passed_argument_name = 'self.cOpaqueStruct!'
201+
native_arguments.append(f'{passed_argument_name}')
202+
203+
struct_methods += f'''
204+
\n\tdeinit {{
205+
{native_call_prep}
206+
\n\t {free_native_name}({', '.join(native_arguments)})
207+
\n\t}}
208+
'''
209+
210+
mutating_output_file_contents = mutating_output_file_contents.replace('class OptionName {',
211+
f'class {swift_struct_name} {{')
212+
mutating_output_file_contents = mutating_output_file_contents.replace('init(pointer: OptionType',
213+
f'init(pointer: {struct_name}')
214+
mutating_output_file_contents = mutating_output_file_contents.replace('var cOpaqueStruct: OptionType?',
215+
f'var cOpaqueStruct: {struct_name}?')
216+
mutating_output_file_contents = method_template_regex.sub(f'\g<1>{struct_methods}\g<3>',
217+
mutating_output_file_contents)
218+
219+
# store the output
220+
output_path = f'{Config.OUTPUT_DIRECTORY_PATH}/options/{swift_struct_name}.swift'
221+
output_directory = os.path.dirname(output_path)
222+
if not os.path.exists(output_directory):
223+
os.makedirs(output_directory)
224+
with open(output_path, "w") as f:
225+
f.write(mutating_output_file_contents)

src/generators/trait_generator.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ def generate_trait(self, struct_name, struct_details):
5959
swift_raw_return_type = current_lambda['return_type'].swift_raw_type
6060
if current_lambda['return_type'].rust_obj is not None and current_lambda['return_type'].rust_obj.startswith('LDK'):
6161
swift_raw_return_type = current_lambda['return_type'].rust_obj
62+
elif current_lambda['return_type'].pass_by_ref and current_lambda_name == 'clone':
63+
swift_raw_return_type = 'UnsafeMutableRawPointer'
6264

6365
current_native_callback_replacement = current_native_callback_replacement.replace(') -> Void {', f') -> {swift_raw_return_type} {{')
6466

src/generators/util_generators/byte_array_generator.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ def __init__(self) -> None:
1111
"(\/\* BYTE_ARRAY_METHODS_START \*\/\n)(.*)(\n[\t ]*\/\* BYTE_ARRAY_METHODS_END \*\/)",
1212
flags=re.MULTILINE | re.DOTALL)
1313
self.loadTemplate()
14+
self.raw_tuple_generators = {}
1415

1516
def generate_byte_array(self, byte_array_type_name, byte_array_type_details):
1617
assert len(byte_array_type_details.fields) == 1
@@ -23,13 +24,31 @@ def generate_byte_array(self, byte_array_type_name, byte_array_type_details):
2324
f'{byte_array_field.var_name}:')
2425
tupleArguments = 'array[0]'
2526
tupleReads = f'nativeType.{byte_array_field.var_name}.0'
27+
rawTupleReads = f'nativeType.0'
2628
for i in range(1, array_length):
2729
tupleArguments += f', array[{i}]'
2830
tupleReads += f', nativeType.{byte_array_field.var_name}.{i}'
31+
rawTupleReads += f', nativeType.{i}'
2932
mutating_current_byte_array_methods = mutating_current_byte_array_methods.replace('tupleArguments',
3033
tupleArguments)
3134
mutating_current_byte_array_methods = mutating_current_byte_array_methods.replace('tupleReads',
3235
tupleReads)
3336
self.filled_template += "\n" + mutating_current_byte_array_methods + "\n"
3437

38+
if not array_length in self.raw_tuple_generators:
39+
self.raw_tuple_generators[array_length] = True
40+
current_generator = f"""
41+
static func array_to_tuple{array_length}(array: [UInt8]) -> {byte_array_field.swift_raw_type} {{
42+
return ({tupleArguments})
43+
}}
44+
45+
static func tuple{array_length}_to_array(nativeType: {byte_array_field.swift_raw_type}) -> [UInt8] {{
46+
let array = [{rawTupleReads}]
47+
return array
48+
}}
49+
"""
50+
51+
self.filled_template += current_generator + "\n"
52+
53+
3554

src/generators/util_generators/vector_generator.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,10 @@ def generate_vector(self, vector_name, vector_type_details):
4242
else:
4343
mutating_current_vector_methods = mutating_current_vector_methods.replace('(convertedEntry)', '(currentEntry)')
4444

45-
if not is_primitive:
45+
if not is_primitive and dimensions > 2 or is_primitive and dimensions > 3:
4646
mutating_current_vector_methods = mutating_current_vector_methods.replace('/* SWIFT_TO_RUST_START */', '/* SWIFT_TO_RUST_START ')
4747
mutating_current_vector_methods = mutating_current_vector_methods.replace('/* SWIFT_TO_RUST_END */', 'SWIFT_TO_RUST_END */')
48+
# pass
4849

4950
mutating_current_vector_methods = mutating_current_vector_methods.replace('SwiftPrimitive',
5051
swift_primitive)

src/lightning_header_parser.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ def populate_type_details(self):
104104

105105
self.trait_structs = set()
106106
self.result_types = set()
107+
self.option_types = set()
107108
self.vec_types = set()
108109
self.byte_arrays = set()
109110

@@ -286,12 +287,10 @@ def populate_type_details(self):
286287
enum_var_name = struct_name.split("_")
287288
union_enum_items[enum_var_name[0]][enum_var_name[1]] = field_lines
288289
elif struct_name in union_enum_items:
289-
# TODO: complex enum
290-
pass
290+
self.option_types.add(struct_name)
291291
elif is_unitary_enum:
292292
self.type_details[struct_name].type = CTypes.UNITARY_ENUM
293293
self.unitary_enums.add(struct_name)
294-
# todo: unitary enums are to be used as is
295294
pass
296295
elif len(trait_fn_lines) > 0:
297296
self.trait_structs.add(struct_name)

src/sdk_generator.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from generators.opaque_struct_generator import OpaqueStructGenerator
33
from generators.tuple_generator import TupleGenerator
44
from generators.result_generator import ResultGenerator
5+
from generators.option_generator import OptionGenerator
56
from generators.trait_generator import TraitGenerator
67
from generators.util_generators.vector_generator import VectorGenerator
78
from generators.util_generators.byte_array_generator import ByteArrayGenerator
@@ -59,6 +60,15 @@ def generate_result_wrappers(parser: LightningHeaderParser):
5960
result_generator.generate_result(current_result, current_result_details, all_type_details=parser.type_details)
6061

6162

63+
def generate_option_wrappers(parser: LightningHeaderParser):
64+
option_generator = OptionGenerator()
65+
66+
options = parser.option_types
67+
for current_option in options:
68+
current_option_details = parser.type_details[current_option]
69+
option_generator.generate_option(current_option, current_option_details, all_type_details=parser.type_details)
70+
71+
6272
def generate_trait_placeholders(parser: LightningHeaderParser):
6373
trait_generator = TraitGenerator()
6474

@@ -74,6 +84,7 @@ def generate_sdk():
7484
generate_opaque_struct_wrappers(parser)
7585
generate_tuple_wrappers(parser)
7686
generate_result_wrappers(parser)
87+
generate_option_wrappers(parser)
7788
generate_trait_placeholders(parser)
7889

7990

0 commit comments

Comments
 (0)