frida 16.7.14 → 16.7.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/README.md +1 -10
  2. package/build/BSDmakefile +6 -0
  3. package/build/Makefile +10 -0
  4. package/build/src/frida.d.ts +364 -0
  5. package/build/src/frida.js +962 -0
  6. package/build/src/frida_binding.d.ts +938 -0
  7. package/meson.build +34 -67
  8. package/package.json +30 -20
  9. package/scripts/fetch-abi-bits.py +15 -65
  10. package/scripts/install.js +5 -4
  11. package/src/addon.def +3 -0
  12. package/src/addon.symbols +2 -1
  13. package/src/addon.version +4 -0
  14. package/src/frida_bindgen/__init__.py +0 -0
  15. package/src/frida_bindgen/__main__.py +4 -0
  16. package/src/frida_bindgen/__pycache__/__init__.cpython-312.pyc +0 -0
  17. package/src/frida_bindgen/__pycache__/__main__.cpython-312.pyc +0 -0
  18. package/src/frida_bindgen/__pycache__/cli.cpython-312.pyc +0 -0
  19. package/src/frida_bindgen/__pycache__/codegen.cpython-312.pyc +0 -0
  20. package/src/frida_bindgen/__pycache__/customization.cpython-312.pyc +0 -0
  21. package/src/frida_bindgen/__pycache__/loader.cpython-312.pyc +0 -0
  22. package/src/frida_bindgen/__pycache__/model.cpython-312.pyc +0 -0
  23. package/src/frida_bindgen/assets/codegen_helpers.c +1970 -0
  24. package/src/frida_bindgen/assets/codegen_helpers.ts +100 -0
  25. package/src/frida_bindgen/assets/codegen_prototypes.h +78 -0
  26. package/src/frida_bindgen/assets/codegen_types.h +57 -0
  27. package/src/frida_bindgen/assets/customization_facade.exports +13 -0
  28. package/src/frida_bindgen/assets/customization_facade.ts +157 -0
  29. package/src/frida_bindgen/assets/customization_helpers.imports +2 -0
  30. package/src/frida_bindgen/assets/customization_helpers.ts +396 -0
  31. package/src/frida_bindgen/cli.py +96 -0
  32. package/src/frida_bindgen/codegen.py +2233 -0
  33. package/src/frida_bindgen/customization.py +924 -0
  34. package/src/frida_bindgen/loader.py +60 -0
  35. package/src/frida_bindgen/model.py +1357 -0
  36. package/src/meson.build +92 -27
  37. package/{lib/build.py → src/tsc.py} +12 -12
  38. package/src/win_delay_load_hook.c +56 -0
  39. package/subprojects/frida-core.wrap +1 -1
  40. package/test/data/index.ts +2 -2
  41. package/test/device.ts +1 -2
  42. package/test/device_manager.ts +1 -2
  43. package/test/labrat.ts +2 -2
  44. package/test/script.ts +12 -12
  45. package/test/session.ts +3 -3
  46. package/tsconfig.json +6 -11
  47. package/dist/application.d.ts +0 -81
  48. package/dist/application.js +0 -2
  49. package/dist/authentication.d.ts +0 -3
  50. package/dist/authentication.js +0 -2
  51. package/dist/bus.d.ts +0 -16
  52. package/dist/bus.js +0 -23
  53. package/dist/cancellable.d.ts +0 -15
  54. package/dist/cancellable.js +0 -41
  55. package/dist/child.d.ts +0 -16
  56. package/dist/child.js +0 -9
  57. package/dist/crash.d.ts +0 -10
  58. package/dist/crash.js +0 -2
  59. package/dist/device.d.ts +0 -156
  60. package/dist/device.js +0 -188
  61. package/dist/device_manager.d.ts +0 -25
  62. package/dist/device_manager.js +0 -42
  63. package/dist/endpoint_parameters.d.ts +0 -26
  64. package/dist/endpoint_parameters.js +0 -24
  65. package/dist/icon.d.ts +0 -14
  66. package/dist/icon.js +0 -2
  67. package/dist/index.d.ts +0 -161
  68. package/dist/index.js +0 -170
  69. package/dist/iostream.d.ts +0 -13
  70. package/dist/iostream.js +0 -73
  71. package/dist/native.d.ts +0 -1
  72. package/dist/native.js +0 -11
  73. package/dist/portal_membership.d.ts +0 -6
  74. package/dist/portal_membership.js +0 -12
  75. package/dist/portal_service.d.ts +0 -48
  76. package/dist/portal_service.js +0 -52
  77. package/dist/process.d.ts +0 -47
  78. package/dist/process.js +0 -2
  79. package/dist/relay.d.ts +0 -22
  80. package/dist/relay.js +0 -32
  81. package/dist/script.d.ts +0 -70
  82. package/dist/script.js +0 -266
  83. package/dist/service.d.ts +0 -16
  84. package/dist/service.js +0 -26
  85. package/dist/session.d.ts +0 -45
  86. package/dist/session.js +0 -73
  87. package/dist/signals.d.ts +0 -20
  88. package/dist/signals.js +0 -40
  89. package/dist/socket_address.d.ts +0 -25
  90. package/dist/socket_address.js +0 -2
  91. package/dist/spawn.d.ts +0 -4
  92. package/dist/spawn.js +0 -2
  93. package/dist/system_parameters.d.ts +0 -84
  94. package/dist/system_parameters.js +0 -2
  95. package/lib/application.ts +0 -98
  96. package/lib/authentication.ts +0 -3
  97. package/lib/bus.ts +0 -30
  98. package/lib/cancellable.ts +0 -48
  99. package/lib/child.ts +0 -15
  100. package/lib/crash.ts +0 -11
  101. package/lib/device.ts +0 -331
  102. package/lib/device_manager.ts +0 -69
  103. package/lib/endpoint_parameters.ts +0 -56
  104. package/lib/icon.ts +0 -15
  105. package/lib/index.ts +0 -316
  106. package/lib/iostream.ts +0 -78
  107. package/lib/meson.build +0 -53
  108. package/lib/native.ts +0 -9
  109. package/lib/portal_membership.ts +0 -10
  110. package/lib/portal_service.ts +0 -105
  111. package/lib/process.ts +0 -57
  112. package/lib/relay.ts +0 -44
  113. package/lib/script.ts +0 -361
  114. package/lib/service.ts +0 -34
  115. package/lib/session.ts +0 -113
  116. package/lib/signals.ts +0 -45
  117. package/lib/socket_address.ts +0 -35
  118. package/lib/spawn.ts +0 -4
  119. package/lib/system_parameters.ts +0 -103
  120. package/meson.options +0 -11
  121. package/src/addon.cc +0 -78
  122. package/src/application.cc +0 -148
  123. package/src/application.h +0 -31
  124. package/src/authentication.cc +0 -174
  125. package/src/authentication.h +0 -24
  126. package/src/bus.cc +0 -167
  127. package/src/bus.h +0 -33
  128. package/src/cancellable.cc +0 -117
  129. package/src/cancellable.h +0 -31
  130. package/src/child.cc +0 -150
  131. package/src/child.h +0 -32
  132. package/src/crash.cc +0 -122
  133. package/src/crash.h +0 -30
  134. package/src/device.cc +0 -1350
  135. package/src/device.h +0 -56
  136. package/src/device_manager.cc +0 -362
  137. package/src/device_manager.h +0 -35
  138. package/src/endpoint_parameters.cc +0 -171
  139. package/src/endpoint_parameters.h +0 -28
  140. package/src/glib_context.cc +0 -62
  141. package/src/glib_context.h +0 -29
  142. package/src/glib_object.cc +0 -25
  143. package/src/glib_object.h +0 -37
  144. package/src/iostream.cc +0 -243
  145. package/src/iostream.h +0 -30
  146. package/src/operation.h +0 -94
  147. package/src/portal_membership.cc +0 -100
  148. package/src/portal_membership.h +0 -26
  149. package/src/portal_service.cc +0 -401
  150. package/src/portal_service.h +0 -40
  151. package/src/process.cc +0 -135
  152. package/src/process.h +0 -30
  153. package/src/relay.cc +0 -139
  154. package/src/relay.h +0 -31
  155. package/src/runtime.cc +0 -568
  156. package/src/runtime.h +0 -69
  157. package/src/script.cc +0 -301
  158. package/src/script.h +0 -36
  159. package/src/service.cc +0 -224
  160. package/src/service.h +0 -36
  161. package/src/session.cc +0 -860
  162. package/src/session.h +0 -42
  163. package/src/signals.cc +0 -334
  164. package/src/signals.h +0 -47
  165. package/src/spawn.cc +0 -95
  166. package/src/spawn.h +0 -27
  167. package/src/usage_monitor.h +0 -117
  168. package/src/uv_context.cc +0 -118
  169. package/src/uv_context.h +0 -40
  170. package/src/win_delay_load_hook.cc +0 -63
  171. package/subprojects/nan.wrap +0 -9
  172. package/subprojects/packagefiles/nan.patch +0 -13
@@ -0,0 +1,2233 @@
1
+ from __future__ import annotations
2
+
3
+ import textwrap
4
+ from pathlib import Path
5
+ from typing import Dict, List, Optional
6
+
7
+ from .model import (ClassObjectType, CustomTypeKind, Enumeration,
8
+ InterfaceObjectType, Method, Model, ObjectType, Parameter,
9
+ Procedure, Property, Signal, Tuple, to_pascal_case)
10
+
11
+ ASSETS_DIR = Path(__file__).resolve().parent / "assets"
12
+ CODEGEN_HELPERS_TS = (ASSETS_DIR / "codegen_helpers.ts").read_text(encoding="utf-8")
13
+ CODEGEN_TYPES_H = (ASSETS_DIR / "codegen_types.h").read_text(encoding="utf-8")
14
+ CODEGEN_PROTOTYPES_H = (ASSETS_DIR / "codegen_prototypes.h").read_text(encoding="utf-8")
15
+ CODEGEN_HELPERS_C = (ASSETS_DIR / "codegen_helpers.c").read_text(encoding="utf-8")
16
+
17
+
18
+ def generate_all(model: Model) -> Dict[str, str]:
19
+ return {
20
+ "ts": generate_ts(model),
21
+ "dts": generate_napi_dts(model),
22
+ "c": generate_napi_bindings(model),
23
+ }
24
+
25
+
26
+ def generate_ts(model: Model) -> str:
27
+ type_imports = []
28
+ for t in model.public_types.values():
29
+ type_imports.append(f"{t.js_name} as _{t.js_name}")
30
+ if isinstance(t, ObjectType):
31
+ for s in t.signals:
32
+ type_imports.append(f"{s.handler_type_name} as _{s.handler_type_name}")
33
+ prefixed_name = s.prefixed_handler_type_name
34
+ if prefixed_name != s.handler_type_name:
35
+ type_imports.append(f"{prefixed_name} as _{prefixed_name}")
36
+ if isinstance(t, InterfaceObjectType) and t.has_abstract_base:
37
+ type_imports.append(f"Abstract{t.js_name} as _Abstract{t.js_name}")
38
+ for name in model.customizations.custom_types.keys():
39
+ type_imports.append(f"{name} as _{name}")
40
+
41
+ lines = [
42
+ 'import bindings from "bindings";',
43
+ "import type {",
44
+ " FridaBinding,",
45
+ *[f" {i}," for i in type_imports],
46
+ " Signal,",
47
+ " SignalHandler,",
48
+ '} from "./frida_binding.d.ts";',
49
+ 'import util from "util";',
50
+ *model.customizations.helper_imports,
51
+ "",
52
+ "const { inspect } = util;",
53
+ ]
54
+
55
+ lines.append(
56
+ """
57
+ const binding: FridaBinding = bindings({
58
+ bindings: "frida_binding",
59
+ try: [
60
+ ["module_root", "bindings"],
61
+ [process.cwd(), "bindings"],
62
+ ]
63
+ });"""
64
+ )
65
+
66
+ for name, cust in model.customizations.custom_types.items():
67
+ if cust.kind == CustomTypeKind.ENUM:
68
+ lines += [
69
+ "",
70
+ f"enum {name}Impl {{",
71
+ indent_ts_code(cust.typing.strip(), 1),
72
+ "}",
73
+ f"(binding as any).{name} = {name}Impl;",
74
+ f"export const {name} = binding.{name};",
75
+ ]
76
+
77
+ lines += [
78
+ "",
79
+ "{",
80
+ indent_ts_code(model.customizations.helper_code.rstrip(), 1),
81
+ indent_ts_code(CODEGEN_HELPERS_TS.rstrip(), 1),
82
+ ]
83
+
84
+ ol = []
85
+ for otype in model.object_types.values():
86
+ if not otype.is_frida_options and not otype.is_frida_list:
87
+ prop_names = ", ".join([f'"{prop.js_name}"' for prop in otype.properties])
88
+ ol += [
89
+ "",
90
+ f"(binding as any).{otype.prefixed_js_name}.prototype[inspect.custom] = function (depth: number, options: util.InspectOptionsStylized): string {{",
91
+ f' return inspectWrapper(this, "{otype.js_name}", [{prop_names}], depth, options);',
92
+ "};",
93
+ ]
94
+
95
+ if not otype.needs_wrapper:
96
+ continue
97
+
98
+ ol += [
99
+ "",
100
+ f"class {otype.js_name} extends binding._{otype.js_name} {{",
101
+ ]
102
+
103
+ num_members = 0
104
+
105
+ custom_code = otype.customizations.custom_code
106
+ if custom_code is not None:
107
+ for declaration in custom_code.declarations:
108
+ if num_members != 0:
109
+ ol.append("")
110
+ ol.append(indent_ts_code(declaration.code.strip(), 1))
111
+ num_members += 1
112
+
113
+ for method in custom_code.methods:
114
+ if num_members != 0:
115
+ ol.append("")
116
+ ol.append(indent_ts_code(method.code.strip(), 1))
117
+ num_members += 1
118
+
119
+ ctor = otype.constructors[0] if otype.constructors else None
120
+ if ctor is not None and ctor.needs_wrapper:
121
+ ol.append(f" constructor({', '.join(ctor.param_typings)}) {{")
122
+
123
+ custom_logic = ctor.customizations.custom_logic
124
+ if custom_logic is not None:
125
+ ol += [
126
+ indent_ts_code(custom_logic.strip(), 2),
127
+ "",
128
+ ]
129
+
130
+ ol += [
131
+ f" super({', '.join(param.js_name for param in ctor.parameters)});",
132
+ " }",
133
+ ]
134
+
135
+ for method in otype.wrapped_methods:
136
+ custom = method.customizations
137
+
138
+ maybe_async = "async " if method.is_async else ""
139
+ maybe_await = "await " if method.is_async else ""
140
+
141
+ if num_members != 0:
142
+ ol.append("")
143
+ ol.append(
144
+ f" {maybe_async}{method.js_name}({', '.join(method.param_typings)}): {method.return_typing} {{"
145
+ )
146
+
147
+ custom_logic = custom.custom_logic
148
+ if custom_logic is not None:
149
+ ol += [
150
+ indent_ts_code(custom_logic.strip(), 2),
151
+ "",
152
+ ]
153
+
154
+ return_capture = "const result = " if method.return_typing != "void" else ""
155
+
156
+ ol.append(
157
+ f" {return_capture}{maybe_await}this._{method.js_name}({', '.join(param.js_name for param in method.input_parameters)});"
158
+ )
159
+
160
+ if return_capture:
161
+ ol.append("")
162
+ return_wrapper = custom.return_wrapper
163
+ if return_wrapper is not None:
164
+ if return_wrapper.startswith("as "):
165
+ ol.append(f" return result {return_wrapper};")
166
+ else:
167
+ ol.append(f" return {return_wrapper}(result);")
168
+ else:
169
+ ol.append(" return result;")
170
+
171
+ ol.append(" }")
172
+
173
+ num_members += 1
174
+
175
+ for signal in otype.wrapped_signals:
176
+ custom = signal.customizations
177
+
178
+ option_lines = []
179
+
180
+ transform = custom.transform
181
+ if transform is not None:
182
+ param_typings = []
183
+ transformed_params = []
184
+ for i, param in enumerate(signal.parameters):
185
+ if transform is not None and i in transform:
186
+ transformed_name_and_type, transform_function = transform[i]
187
+ param_typings.append(transformed_name_and_type)
188
+ if transform_function is not None:
189
+ transformed_params.append(
190
+ f"{transform_function}({param.js_name})"
191
+ )
192
+ else:
193
+ transformed_params.append(param.js_name)
194
+ else:
195
+ param_typings.append(param.typing)
196
+ transformed_params.append(param.js_name)
197
+
198
+ transformed_params_str = ", ".join(transformed_params)
199
+
200
+ option_lines += [
201
+ f"transform({', '.join(p.js_name for p in signal.parameters)}) {{",
202
+ f" return [{transformed_params_str}];",
203
+ "},",
204
+ ]
205
+
206
+ intercept = custom.intercept
207
+ if intercept is not None:
208
+ option_lines.append(f"intercept: {intercept},")
209
+
210
+ if num_members != 0:
211
+ ol.append("")
212
+ option_indent = 8 * " "
213
+ ol += [
214
+ f" {signal.js_name}: Signal<_{signal.handler_type_name}> = new SignalWrapper<__{signal.handler_type_name}, _{signal.handler_type_name}>(this._{signal.js_name}, {{",
215
+ *[option_indent + line for line in option_lines],
216
+ " });",
217
+ ]
218
+
219
+ num_members += 1
220
+
221
+ ol += [
222
+ "}",
223
+ "",
224
+ f"binding.{otype.js_name} = {otype.js_name};",
225
+ ]
226
+
227
+ lines += [
228
+ indent_ts_code("\n".join(ol), 1),
229
+ "}",
230
+ "",
231
+ "binding.commitConstructors();",
232
+ "",
233
+ "export const {",
234
+ ]
235
+ for t in model.public_types.values():
236
+ if isinstance(t, InterfaceObjectType):
237
+ if t.has_abstract_base:
238
+ lines.append(f" Abstract{t.js_name},")
239
+ continue
240
+ lines.append(f" {t.js_name},")
241
+ lines += [
242
+ "} = binding;",
243
+ "",
244
+ "const frida = {",
245
+ ]
246
+ for t in model.public_types.values():
247
+ if isinstance(t, InterfaceObjectType):
248
+ if t.has_abstract_base:
249
+ lines.append(f" Abstract{t.js_name},")
250
+ continue
251
+ lines.append(f" {t.js_name},")
252
+ for name, cust in model.customizations.custom_types.items():
253
+ if cust.kind == CustomTypeKind.ENUM:
254
+ lines.append(f" {name},")
255
+ lines += [
256
+ *[f" {e}," for e in model.customizations.facade_exports],
257
+ "} as const;",
258
+ "",
259
+ "export default frida;",
260
+ ]
261
+
262
+ type_exports = []
263
+ for t in model.public_types.values():
264
+ type_exports.append(f"export type {t.js_name} = _{t.js_name};")
265
+ if isinstance(t, ObjectType):
266
+ if isinstance(t, InterfaceObjectType) and t.has_abstract_base:
267
+ type_exports.append(
268
+ f"export type Abstract{t.js_name} = _Abstract{t.js_name};"
269
+ )
270
+ type_exports += [
271
+ f"export type {s.handler_type_name} = _{s.handler_type_name};"
272
+ for s in t.signals
273
+ ]
274
+ for name in model.customizations.custom_types.keys():
275
+ type_exports.append(f"export type {name} = _{name};")
276
+
277
+ lines += [
278
+ "",
279
+ "namespace frida {",
280
+ ]
281
+ for e in type_exports:
282
+ lines.append(indent_ts_code(e, 1))
283
+ lines += [
284
+ "}",
285
+ "",
286
+ ]
287
+ for e in type_exports:
288
+ lines.append(e)
289
+
290
+ lines.append("")
291
+ lines.append(model.customizations.facade_code)
292
+
293
+ return "\n".join(lines)
294
+
295
+
296
+ def generate_napi_dts(model: Model) -> str:
297
+ lines = [
298
+ "export interface FridaBinding {",
299
+ " commitConstructors(): void;",
300
+ ]
301
+ for t in model.public_types.values():
302
+ if t.is_frida_options:
303
+ lines.append(f" {t.prefixed_js_name}: {t.prefixed_js_name};")
304
+ else:
305
+ if isinstance(t, InterfaceObjectType):
306
+ if t.has_abstract_base:
307
+ lines.append(
308
+ f" Abstract{t.js_name}: typeof Abstract{t.js_name};"
309
+ )
310
+ continue
311
+ if isinstance(t, ClassObjectType):
312
+ if t.needs_wrapper:
313
+ lines.append(f" {t.js_name}: typeof {t.js_name};")
314
+ lines.append(f" {t.prefixed_js_name}: typeof {t.prefixed_js_name};")
315
+ for name, cust in model.customizations.custom_types.items():
316
+ if cust.kind == CustomTypeKind.ENUM:
317
+ lines.append(f" {name}: typeof {name};")
318
+ lines.append("}")
319
+
320
+ for otype in model.object_types.values():
321
+ if not otype.is_public:
322
+ continue
323
+
324
+ if otype.needs_wrapper:
325
+ lines += [
326
+ "",
327
+ f"export class {otype.js_name} extends {otype.prefixed_js_name} {{",
328
+ ]
329
+
330
+ custom_code = otype.customizations.custom_code
331
+ if custom_code is not None:
332
+ for declaration in custom_code.declarations:
333
+ typing = declaration.typing
334
+ if typing is not None:
335
+ lines.append(f" {typing};")
336
+
337
+ for method in custom_code.methods:
338
+ typing = method.typing
339
+ if typing is not None:
340
+ lines.append(f" {typing};")
341
+
342
+ ctor = otype.constructors[0] if otype.constructors else None
343
+ if ctor is not None and ctor.needs_wrapper:
344
+ lines.append(f" constructor({', '.join(ctor.param_typings)});")
345
+
346
+ for method in otype.wrapped_methods:
347
+ params = ", ".join(method.param_typings)
348
+ lines.append(f" {method.js_name}({params}): {method.return_typing};")
349
+
350
+ for signal in otype.wrapped_signals:
351
+ lines.append(
352
+ f" readonly {signal.js_name}: Signal<{signal.handler_type_name}>;"
353
+ )
354
+
355
+ lines.append("}")
356
+
357
+ if otype.wrapped_signals:
358
+ lines.append("")
359
+ for signal in otype.wrapped_signals:
360
+ params = ", ".join(
361
+ signal.customizations.transform.get(i, (param.typing, ""))[0]
362
+ for i, param in enumerate(signal.parameters)
363
+ )
364
+ lines.append(
365
+ f"export type {signal.handler_type_name} = ({params}) => void;"
366
+ )
367
+
368
+ lines.append("")
369
+
370
+ parent = otype.parent
371
+ parent_name = parent.js_name if parent is not None else None
372
+ extends = (
373
+ ""
374
+ if (parent_name is None or otype.is_frida_options)
375
+ else f" extends {parent_name}"
376
+ )
377
+ if isinstance(otype, InterfaceObjectType) or otype.is_frida_options:
378
+ lines.append(f"export interface {otype.prefixed_js_name}{extends} {{")
379
+ else:
380
+ implements = (
381
+ f" implements {', '.join([t.js_name for t in otype.implements])}"
382
+ if otype.implements
383
+ else ""
384
+ )
385
+ lines.append(
386
+ f"export class {otype.prefixed_js_name}{extends}{implements} {{"
387
+ )
388
+
389
+ if otype.constructors:
390
+ constructor = otype.constructors[0]
391
+ params = ", ".join(param.typing for param in constructor.parameters)
392
+ lines.append(f" constructor({params});")
393
+
394
+ if otype.is_frida_options:
395
+ for method in otype.methods:
396
+ if method.is_select_method:
397
+ lines.append(
398
+ f" {method.select_plural_noun}?: {model.resolve_js_type(method.select_element_type)}[];"
399
+ )
400
+ else:
401
+ for method in otype.methods:
402
+ if method.is_property_accessor:
403
+ continue
404
+ visibility = (
405
+ "protected " if method.prefixed_js_name != method.js_name else ""
406
+ )
407
+ lines.append(
408
+ f" {visibility}{method.prefixed_js_name}({', '.join(method.prefixed_param_typings)}): {method.prefixed_return_typing};"
409
+ )
410
+
411
+ for prop in otype.properties:
412
+ lines.append(f" {prop.typing};")
413
+
414
+ for signal in otype.signals:
415
+ visibility = (
416
+ "protected " if signal.prefixed_js_name != signal.js_name else ""
417
+ )
418
+ lines.append(
419
+ f" {visibility}readonly {signal.prefixed_js_name}: Signal<{signal.prefixed_handler_type_name}>;"
420
+ )
421
+
422
+ if isinstance(otype, ClassObjectType):
423
+ for itype in otype.implements:
424
+ for method in itype.methods:
425
+ lines.append(
426
+ f" {method.js_name}({', '.join(method.param_typings)}): {method.return_typing};"
427
+ )
428
+
429
+ for prop in itype.properties:
430
+ lines.append(f" {prop.typing};")
431
+
432
+ for signal in itype.signals:
433
+ lines.append(
434
+ f" readonly {signal.js_name}: Signal<{signal.handler_type_name}>;"
435
+ )
436
+
437
+ lines.append("}")
438
+
439
+ if otype.signals:
440
+ lines.append("")
441
+ for signal in otype.signals:
442
+ lines.append(
443
+ f"export type {signal.prefixed_handler_type_name} = {signal.typing};"
444
+ )
445
+
446
+ if isinstance(otype, InterfaceObjectType) and otype.has_abstract_base:
447
+ lines.append("")
448
+
449
+ object_js_name = model.resolve_object_type("Object").js_name
450
+ lines.append(
451
+ f"export abstract class Abstract{otype.js_name} extends {object_js_name} implements {otype.js_name} {{"
452
+ )
453
+
454
+ for method in otype.methods:
455
+ params = ", ".join([t.replace("?:", ":") for t in method.param_typings])
456
+ lines.append(f" {method.js_name}({params}): {method.return_typing};")
457
+
458
+ for prop in otype.properties:
459
+ lines.append(f" {prop.typing};")
460
+
461
+ for signal in otype.signals:
462
+ lines.append(
463
+ f" readonly {signal.js_name}: Signal<{signal.handler_type_name}>;"
464
+ )
465
+
466
+ lines.append("}")
467
+
468
+ for enum in model.enumerations.values():
469
+ members = ",\n ".join(
470
+ f'{member.js_name} = "{member.nick}"' for member in enum.members
471
+ )
472
+ lines += [
473
+ "",
474
+ f"export enum {enum.js_name} {{",
475
+ f" {members}",
476
+ "}",
477
+ ]
478
+
479
+ for name, cust in model.customizations.custom_types.items():
480
+ code = f"\nexport {cust.kind.value} {name}"
481
+ if cust.kind == CustomTypeKind.TYPE:
482
+ code += " = "
483
+ code += cust.typing.strip()
484
+ code += ";"
485
+ else:
486
+ code += " {\n"
487
+ code += indent_ts_code(cust.typing.strip(), 1)
488
+ code += "\n}"
489
+ lines.append(code)
490
+
491
+ lines.append(
492
+ """
493
+ export class Signal<H extends SignalHandler> {
494
+ connect(handler: H): void;
495
+ disconnect(handler: H): void;
496
+ }
497
+
498
+ export type SignalHandler = (...args: any[]) => void;
499
+ """
500
+ )
501
+
502
+ return "\n".join(lines)
503
+
504
+
505
+ def generate_napi_bindings(model: Model) -> str:
506
+ object_types = model.object_types.values()
507
+ enumerations = model.enumerations.values()
508
+
509
+ code = generate_includes()
510
+ code += generate_abstract_base_type_declarations(model)
511
+ code += generate_operation_structs(object_types)
512
+ code += CODEGEN_TYPES_H
513
+ code += generate_prototypes(object_types, enumerations)
514
+ code += generate_abstract_base_define_type_invocations(model)
515
+ code += generate_shared_globals()
516
+ code += generate_type_tags(object_types)
517
+ code += generate_constructor_declarations(object_types)
518
+ code += generate_tsfn_declarations(object_types)
519
+ code += generate_init_function(object_types, enumerations)
520
+ code += generate_commit_constructors_function(object_types)
521
+
522
+ for otype in object_types:
523
+ if otype.is_frida_options:
524
+ code += generate_options_conversion_functions(otype)
525
+ continue
526
+ if otype.is_frida_list:
527
+ code += generate_list_conversion_functions(otype)
528
+ continue
529
+
530
+ code += generate_object_type_registration_code(otype, model)
531
+ code += generate_object_type_conversion_functions(otype)
532
+ code += generate_object_type_constructor(otype)
533
+ code += generate_object_type_finalizer(otype)
534
+ code += generate_object_type_cleanup_code(otype)
535
+
536
+ for method in otype.methods:
537
+ code += generate_method_code(method)
538
+
539
+ for signal in otype.signals:
540
+ code += generate_signal_getter_code(otype, signal)
541
+
542
+ if isinstance(otype, InterfaceObjectType) and otype.has_abstract_base:
543
+ code += generate_abstract_base_registration_code(otype)
544
+ code += generate_abstract_base_constructor(otype)
545
+ code += generate_abstract_base_gobject_glue(otype)
546
+ for method in otype.methods:
547
+ code += generate_abstract_base_method_code(method)
548
+
549
+ for enum in enumerations:
550
+ code += generate_enum_registration_code(enum)
551
+ code += generate_enum_conversion_functions(enum)
552
+
553
+ code += CODEGEN_HELPERS_C
554
+
555
+ return code
556
+
557
+
558
+ def generate_includes() -> str:
559
+ return """\
560
+ #include <frida-core.h>
561
+ #include <node_api.h>
562
+ #include <string.h>
563
+
564
+ """
565
+
566
+
567
+ def generate_operation_structs(object_types: List[ObjectType]) -> str:
568
+ structs = []
569
+
570
+ for otype in object_types:
571
+ is_iface_with_abstract_base = (
572
+ isinstance(otype, InterfaceObjectType) and otype.has_abstract_base
573
+ )
574
+
575
+ for method in otype.methods:
576
+ if method.is_async:
577
+ param_declarations = generate_parameter_variable_declarations(method)
578
+ return_declaration = generate_return_variable_declaration(method)
579
+ decls = "".join(
580
+ [
581
+ indent_c_code(param_declarations, 1, prologue="\n"),
582
+ indent_c_code(return_declaration, 1, prologue="\n"),
583
+ ]
584
+ )
585
+ structs.append(
586
+ f"""\
587
+ typedef struct {{
588
+ napi_deferred deferred;
589
+ {otype.c_type} * handle;{decls}
590
+ }} {method.operation_type_name};
591
+ """
592
+ )
593
+
594
+ if is_iface_with_abstract_base:
595
+ param_declarations = generate_parameter_variable_declarations(method)
596
+ return_declaration = generate_return_variable_declaration(method)
597
+ decls = "".join(
598
+ [
599
+ indent_c_code(param_declarations, 1, prologue="\n"),
600
+ indent_c_code(return_declaration, 1, prologue="\n"),
601
+ ]
602
+ )
603
+ structs.append(
604
+ f"""\
605
+ typedef struct {{
606
+ {otype.abstract_base_c_type} * self;{decls}
607
+ }} {method.abstract_base_operation_type_name};
608
+ """
609
+ )
610
+
611
+ return "\n".join(structs)
612
+
613
+
614
+ def generate_prototypes(
615
+ object_types: List[ObjectType], enumerations: List[Enumeration]
616
+ ) -> str:
617
+ prototypes = [
618
+ "static void fdn_deinit (void * data);",
619
+ "",
620
+ "static napi_value fdn_commit_constructors (napi_env env, napi_callback_info info);",
621
+ ]
622
+
623
+ for otype in object_types:
624
+ otype_cprefix = otype.c_symbol_prefix
625
+
626
+ prototypes.append("")
627
+
628
+ if not otype.is_frida_options and not otype.is_frida_list:
629
+ prototypes.append(
630
+ f"static void {otype_cprefix}_register (napi_env env, napi_value exports);"
631
+ )
632
+
633
+ if not otype.is_frida_list:
634
+ prototypes.append(
635
+ f"G_GNUC_UNUSED static gboolean {otype_cprefix}_from_value (napi_env env, napi_value value, {otype.c_type} ** handle);"
636
+ )
637
+
638
+ if not otype.is_frida_options:
639
+ prototypes += [
640
+ f"G_GNUC_UNUSED static napi_value {otype_cprefix}_to_value (napi_env env, {otype.c_type} * handle);",
641
+ ]
642
+
643
+ if not otype.is_frida_options and not otype.is_frida_list:
644
+ prototypes.append(
645
+ f"static napi_value {otype_cprefix}_construct (napi_env env, napi_callback_info info);"
646
+ )
647
+
648
+ custom = otype.customizations
649
+ if custom is not None and custom.cleanup is not None:
650
+ prototypes += [
651
+ f"static void {otype_cprefix}_finalize (napi_env env, void * finalize_data, void * finalize_hint);",
652
+ "",
653
+ f"static void {otype.c_symbol_prefix}_handle_cleanup (void * data);",
654
+ ]
655
+
656
+ for method in otype.methods:
657
+ method_cprefix = f"{otype_cprefix}_{method.name}"
658
+ prototypes += [
659
+ "",
660
+ f"static napi_value {method_cprefix} (napi_env env, napi_callback_info info);",
661
+ ]
662
+ if method.is_async:
663
+ prototypes += [
664
+ f"static gboolean {method_cprefix}_begin (gpointer user_data);",
665
+ f"static void {method_cprefix}_end (GObject * source_object, GAsyncResult * res, gpointer user_data);",
666
+ f"static void {method_cprefix}_deliver (napi_env env, napi_value js_cb, void * context, void * data);",
667
+ f"static void {method_cprefix}_operation_free ({method.operation_type_name} * operation);",
668
+ ]
669
+
670
+ for i, signal in enumerate(otype.signals):
671
+ if i == 0:
672
+ prototypes.append("")
673
+ prototypes.append(
674
+ f"static napi_value {otype_cprefix}_get_{signal.c_name}_signal (napi_env env, napi_callback_info info);"
675
+ )
676
+
677
+ if isinstance(otype, InterfaceObjectType) and otype.has_abstract_base:
678
+ cprefix = otype.abstract_base_c_symbol_prefix
679
+
680
+ prototypes += [
681
+ "",
682
+ f"static void {cprefix}_register (napi_env env, napi_value exports);",
683
+ f"static napi_value {cprefix}_construct (napi_env env, napi_callback_info info);",
684
+ f"static void {cprefix}_iface_init (gpointer g_iface, gpointer iface_data);",
685
+ f"static void {cprefix}_dispose (GObject * object);",
686
+ f"static void {cprefix}_release_js_resources (napi_env env, napi_value js_cb, void * context, void * data);",
687
+ ]
688
+
689
+ for m in otype.methods:
690
+ method_cprefix = f"{cprefix}_{m.name}"
691
+ prototypes += [
692
+ f"static void {method_cprefix} ({', '.join(m.param_ctypings)});",
693
+ f"static void {method_cprefix}_operation_free ({m.abstract_base_operation_type_name} * operation);",
694
+ f"static void {method_cprefix}_begin (napi_env env, napi_value js_cb, void * context, void * data);",
695
+ f"static napi_value {method_cprefix}_on_success (napi_env env, napi_callback_info info);",
696
+ f"static napi_value {method_cprefix}_on_failure (napi_env env, napi_callback_info info);",
697
+ f"static {m.return_ctyping} {method_cprefix}_finish ({', '.join(m.finish_param_ctypings)});",
698
+ ]
699
+
700
+ for enum in enumerations:
701
+ enum_cprefix = enum.c_symbol_prefix
702
+ prototypes += [
703
+ "",
704
+ f"static void {enum_cprefix}_register (napi_env env, napi_value exports);",
705
+ f"G_GNUC_UNUSED static gboolean {enum_cprefix}_from_value (napi_env env, napi_value value, {enum.c_type} * e);",
706
+ f"G_GNUC_UNUSED static napi_value {enum_cprefix}_to_value (napi_env env, {enum.c_type} e);",
707
+ ]
708
+
709
+ prototypes.append(CODEGEN_PROTOTYPES_H.rstrip())
710
+
711
+ return "\n".join(prototypes) + "\n\n"
712
+
713
+
714
+ def generate_shared_globals() -> str:
715
+ return "\n".join(
716
+ [
717
+ "static napi_ref fdn_exports;",
718
+ "static GHashTable * fdn_constructors;",
719
+ "static gboolean fdn_in_cleanup = FALSE;",
720
+ "",
721
+ "",
722
+ ]
723
+ )
724
+
725
+
726
+ def generate_type_tags(object_types: List[ObjectType]) -> str:
727
+ type_tags = [
728
+ "static napi_type_tag fdn_handle_wrapper_type_tag = { 0xdd596d4f2dad45f9, 0x844585a48e8d05ba };",
729
+ "static napi_type_tag fdn_object_type_tag = { 0x4eeacfcdc22c425a, 0x91346eafdc89fedc };",
730
+ ]
731
+ return "\n".join(type_tags) + "\n"
732
+
733
+
734
+ def generate_constructor_declarations(object_types: List[ObjectType]) -> str:
735
+ declarations = []
736
+
737
+ for otype in object_types:
738
+ if otype.is_frida_options or otype.is_frida_list:
739
+ continue
740
+ declarations.append(f"static napi_ref {otype.c_symbol_prefix}_constructor;")
741
+
742
+ declarations += [
743
+ "",
744
+ "static napi_ref fdn_signal_constructor;",
745
+ ]
746
+
747
+ return "\n" + "\n".join(declarations) + "\n"
748
+
749
+
750
+ def generate_tsfn_declarations(object_types: List[ObjectType]) -> str:
751
+ declarations = []
752
+
753
+ for otype in object_types:
754
+ async_methods = [method for method in otype.methods if method.is_async]
755
+ if async_methods:
756
+ declarations.append("")
757
+ for method in async_methods:
758
+ declarations.append(
759
+ f"static napi_threadsafe_function {otype.c_symbol_prefix}_{method.name}_tsfn;"
760
+ )
761
+
762
+ if isinstance(otype, InterfaceObjectType) and otype.has_abstract_base:
763
+ declarations.append(
764
+ f"static napi_threadsafe_function {otype.abstract_base_c_symbol_prefix}_release_js_resources_tsfn;"
765
+ )
766
+ for method in otype.methods:
767
+ declarations.append(
768
+ f"static napi_threadsafe_function {otype.abstract_base_c_symbol_prefix}_{method.name}_tsfn;"
769
+ )
770
+
771
+ declarations += [
772
+ "",
773
+ "static napi_threadsafe_function fdn_keep_alive_tsfn;",
774
+ ]
775
+
776
+ return "\n".join(declarations) + "\n"
777
+
778
+
779
+ def generate_init_function(
780
+ object_types: List[ObjectType], enumerations: List[Enumeration]
781
+ ) -> str:
782
+ object_type_prefixes = []
783
+ for otype in object_types:
784
+ if otype.is_frida_options or otype.is_frida_list:
785
+ continue
786
+ object_type_prefixes.append(otype.c_symbol_prefix)
787
+ if isinstance(otype, InterfaceObjectType) and otype.has_abstract_base:
788
+ object_type_prefixes.append(otype.abstract_base_c_symbol_prefix)
789
+ object_type_registration_calls = "\n ".join(
790
+ [f"{prefix}_register (env, exports);" for prefix in object_type_prefixes]
791
+ )
792
+
793
+ enum_type_registration_calls = "\n ".join(
794
+ [f"{enum.c_symbol_prefix}_register (env, exports);" for enum in enumerations]
795
+ )
796
+
797
+ return f"""
798
+ static napi_value
799
+ fdn_init (napi_env env,
800
+ napi_value exports)
801
+ {{
802
+ napi_value commit_ctors;
803
+
804
+ frida_init ();
805
+
806
+ napi_create_reference (env, exports, 1, &fdn_exports);
807
+ fdn_constructors = g_hash_table_new (NULL, NULL);
808
+
809
+ napi_create_function (env, "commitConstructors", NAPI_AUTO_LENGTH, fdn_commit_constructors, NULL, &commit_ctors);
810
+ napi_set_named_property (env, exports, "commitConstructors", commit_ctors);
811
+
812
+ {object_type_registration_calls}
813
+
814
+ {enum_type_registration_calls}
815
+
816
+ fdn_signal_register (env, exports);
817
+
818
+ napi_create_threadsafe_function (env, NULL, NULL, fdn_utf8_to_value (env, "FridaKeepAlive"), 0, 1, NULL, NULL, NULL, fdn_keep_alive_on_tsfn_invoke, &fdn_keep_alive_tsfn);
819
+ napi_unref_threadsafe_function (env, fdn_keep_alive_tsfn);
820
+
821
+ napi_add_env_cleanup_hook (env, fdn_deinit, NULL);
822
+
823
+ return exports;
824
+ }}
825
+
826
+ static void
827
+ fdn_deinit (void * data)
828
+ {{
829
+ fdn_in_cleanup = TRUE;
830
+ }}
831
+
832
+ NAPI_MODULE (NODE_GYP_MODULE_NAME, fdn_init)
833
+ """
834
+
835
+
836
+ def generate_commit_constructors_function(object_types: List[ObjectType]) -> str:
837
+ commits = ""
838
+ for otype in object_types:
839
+ if otype.is_frida_options or otype.is_frida_list:
840
+ continue
841
+
842
+ otype_cprefix = otype.c_symbol_prefix
843
+
844
+ if otype.needs_wrapper:
845
+ commits += f""" if ({otype_cprefix}_constructor == NULL)
846
+ {{
847
+ napi_get_named_property (env, exports, "{otype.js_name}", &ctor);
848
+ napi_create_reference (env, ctor, 1, &{otype_cprefix}_constructor);
849
+ }}
850
+ """
851
+
852
+ commits += f" g_hash_table_insert (fdn_constructors, GSIZE_TO_POINTER ({otype.get_type} ()), {otype_cprefix}_constructor);\n\n"
853
+
854
+ inherits = ""
855
+ for otype in object_types:
856
+ if otype.is_frida_options or otype.is_frida_list:
857
+ continue
858
+
859
+ parent = otype.parent
860
+ if parent is None:
861
+ continue
862
+
863
+ if otype.needs_wrapper:
864
+ inherits += f"""
865
+ napi_get_named_property (env, exports, "{otype.prefixed_js_name}", &ctor);
866
+ fdn_inherit_ref_val (env, {otype.c_symbol_prefix}_constructor, ctor, object_ctor, set_proto);
867
+ """
868
+ if parent.name == "Object":
869
+ inherits += " fdn_inherit_val_val (env, ctor, fdn_object_ctor, object_ctor, set_proto);"
870
+ else:
871
+ inherits += " fdn_inherit_val_ref (env, ctor, {parent.c_symbol_prefix}_constructor, object_ctor, set_proto);"
872
+ else:
873
+ if parent.name == "Object":
874
+ inherits += f"""
875
+ fdn_inherit_ref_val (env, {otype.c_symbol_prefix}_constructor, fdn_object_ctor, object_ctor, set_proto);
876
+ """
877
+ else:
878
+ inherits += f"""
879
+ fdn_inherit_ref_ref (env, {otype.c_symbol_prefix}_constructor, {parent.c_symbol_prefix}_constructor, object_ctor, set_proto);
880
+ """
881
+
882
+ return f"""
883
+ static napi_value
884
+ fdn_commit_constructors (napi_env env,
885
+ napi_callback_info info)
886
+ {{
887
+ napi_value result, exports, ctor, global, object_ctor, set_proto, fdn_object_ctor;
888
+
889
+ napi_get_reference_value (env, fdn_exports, &exports);
890
+ {commits} napi_get_global (env, &global);
891
+ napi_get_named_property (env, global, "Object", &object_ctor);
892
+ napi_get_named_property (env, object_ctor, "setPrototypeOf", &set_proto);
893
+
894
+ napi_get_reference_value (env, fdn_object_constructor, &fdn_object_ctor);
895
+ {inherits}
896
+ napi_get_undefined (env, &result);
897
+ return result;
898
+ }}
899
+ """
900
+
901
+
902
+ def generate_object_type_registration_code(otype: ObjectType, model: Model) -> str:
903
+ otype_cprefix = otype.c_symbol_prefix
904
+
905
+ ctor_ref_creation = (
906
+ ""
907
+ if otype.needs_wrapper
908
+ else f"\n napi_create_reference (env, constructor, 1, &{otype_cprefix}_constructor);"
909
+ )
910
+ jsprop_registrations = []
911
+ tsfn_initializations = []
912
+
913
+ for method in otype.methods:
914
+ if method.is_property_accessor:
915
+ continue
916
+ jsprop_registrations.append(generate_method_registration_entry(method))
917
+ if method.is_async:
918
+ tsfn_initializations.append(
919
+ f"""\
920
+ napi_create_threadsafe_function (env, NULL, NULL, fdn_utf8_to_value (env, "{method.prefixed_js_name}"), 0, 1, NULL, NULL, NULL, {otype_cprefix}_{method.name}_deliver, &{otype_cprefix}_{method.name}_tsfn);
921
+ napi_unref_threadsafe_function (env, {otype_cprefix}_{method.name}_tsfn);"""
922
+ )
923
+
924
+ for prop in otype.properties:
925
+ jsprop_registrations.append(generate_property_registration_entry(prop))
926
+
927
+ for signal in otype.signals:
928
+ jsprop_registrations.append(generate_signal_registration_entry(signal))
929
+
930
+ if isinstance(otype, ClassObjectType):
931
+ for itype in otype.implements:
932
+ for method in itype.methods:
933
+ if method.is_property_accessor:
934
+ continue
935
+ jsprop_registrations.append(generate_method_registration_entry(method))
936
+
937
+ for prop in itype.properties:
938
+ jsprop_registrations.append(generate_property_registration_entry(prop))
939
+
940
+ for signal in itype.signals:
941
+ jsprop_registrations.append(generate_signal_registration_entry(signal))
942
+
943
+ jsprop_registrations_str = "\n ".join(jsprop_registrations)
944
+ tsfn_initializations_code = "\n\n".join(tsfn_initializations)
945
+
946
+ def calculate_indent(suffix: str) -> str:
947
+ return " " * (len(otype_cprefix) + len(suffix) + 2)
948
+
949
+ two_newlines = "\n\n"
950
+
951
+ return f"""
952
+ static void
953
+ {otype_cprefix}_register (napi_env env,
954
+ {calculate_indent("_register")}napi_value exports)
955
+ {{
956
+ napi_property_descriptor properties[] =
957
+ {{
958
+ {jsprop_registrations_str}
959
+ }};
960
+ napi_value constructor;
961
+
962
+ napi_define_class (env, "{otype.prefixed_js_name}", NAPI_AUTO_LENGTH, {otype_cprefix}_construct, NULL, G_N_ELEMENTS (properties), properties, &constructor);{ctor_ref_creation}
963
+
964
+ napi_set_named_property (env, exports, "{otype.prefixed_js_name}", constructor);{indent_c_code(tsfn_initializations_code, 1, prologue=two_newlines)}
965
+ }}
966
+ """
967
+
968
+
969
+ def generate_method_registration_entry(method: Method) -> str:
970
+ return f'{{ "{method.prefixed_js_name}", NULL, {method.object_type.c_symbol_prefix}_{method.name}, NULL, NULL, NULL, napi_default, NULL }},'
971
+
972
+
973
+ def generate_property_registration_entry(prop: Property) -> str:
974
+ otype_cprefix = prop.object_type.c_symbol_prefix
975
+
976
+ setter_str = f"{otype_cprefix}_{prop.setter}" if prop.setter is not None else "NULL"
977
+
978
+ attrs = ["enumerable", "configurable"]
979
+ if prop.setter is not None:
980
+ attrs.insert(0, "writable")
981
+ attrs_str = " | ".join([f"napi_{attr}" for attr in attrs])
982
+
983
+ return f'{{ "{prop.js_name}", NULL, NULL, {otype_cprefix}_{prop.getter}, {setter_str}, NULL, {attrs_str}, NULL }},'
984
+
985
+
986
+ def generate_signal_registration_entry(signal: Signal) -> str:
987
+ return f'{{ "{signal.prefixed_js_name}", NULL, NULL, {signal.object_type.c_symbol_prefix}_get_{signal.c_name}_signal, NULL, NULL, napi_default, NULL }},'
988
+
989
+
990
+ def generate_object_type_conversion_functions(otype: ObjectType) -> str:
991
+ otype_cprefix = otype.c_symbol_prefix
992
+
993
+ def calculate_indent(suffix: str) -> str:
994
+ return " " * (len(otype_cprefix) + len(suffix) + 2)
995
+
996
+ from_value_function = f"""
997
+ static gboolean
998
+ {otype_cprefix}_from_value (napi_env env,
999
+ {calculate_indent("_from_value")}napi_value value,
1000
+ {calculate_indent("_from_value")}{otype.c_type} ** handle)
1001
+ {{
1002
+ return fdn_object_unwrap (env, value, {otype.get_type} (), (GObject **) handle);
1003
+ }}
1004
+ """
1005
+
1006
+ to_value_function = f"""
1007
+ static napi_value
1008
+ {otype_cprefix}_to_value (napi_env env,
1009
+ {calculate_indent("_to_value")}{otype.c_type} * handle)
1010
+ {{
1011
+ return fdn_object_new (env, G_OBJECT (handle), {otype_cprefix}_constructor);
1012
+ }}
1013
+ """
1014
+
1015
+ return from_value_function + to_value_function
1016
+
1017
+
1018
+ def generate_object_type_constructor(otype: ObjectType) -> str:
1019
+ otype_cprefix = otype.c_symbol_prefix
1020
+
1021
+ def calculate_indent(suffix: str) -> str:
1022
+ return " " * (len(otype_cprefix) + len(suffix) + 2)
1023
+
1024
+ ctor = next(iter(otype.constructors), None)
1025
+
1026
+ n_parameters = max(len(ctor.parameters) if ctor is not None else 0, 1)
1027
+
1028
+ storage_prefix = ""
1029
+ invalid_arg_label = "propagate_error"
1030
+ error_check = ""
1031
+ construction_failed_logic = ""
1032
+
1033
+ if ctor is not None:
1034
+ param_declarations = generate_parameter_variable_declarations(
1035
+ ctor, initialize=True
1036
+ )
1037
+ if ctor.parameters:
1038
+ param_conversions = generate_input_parameter_conversions_code(
1039
+ ctor, storage_prefix, invalid_arg_label
1040
+ )
1041
+ else:
1042
+ param_conversions = """if (argc != 0)
1043
+ goto invalid_handle;"""
1044
+ param_destructions = generate_parameter_destructions_code(ctor, storage_prefix)
1045
+
1046
+ call_args = generate_call_arguments_code(ctor, storage_prefix)
1047
+ constructor_call = (
1048
+ f"handle = {otype.c_cast_macro} ({ctor.c_identifier} ({call_args}));"
1049
+ )
1050
+ unconstructable_logic = ""
1051
+
1052
+ if ctor.throws:
1053
+ error_check = """if (error != NULL)
1054
+ goto construction_failed;"""
1055
+ construction_failed_logic = """construction_failed:
1056
+ {
1057
+ napi_throw (env, fdn_error_to_value (env, error));
1058
+ g_error_free (error);
1059
+ goto propagate_error;
1060
+ }
1061
+ """
1062
+ else:
1063
+ param_declarations = ""
1064
+ param_conversions = """if (argc == 0)
1065
+ goto unconstructable;
1066
+
1067
+ goto invalid_handle;"""
1068
+ param_destructions = ""
1069
+ constructor_call = ""
1070
+ unconstructable_logic = f"""unconstructable:
1071
+ {{
1072
+ napi_throw_error (env, NULL, "type {otype.js_name} cannot be constructed");
1073
+ return NULL;
1074
+ }}
1075
+ """
1076
+
1077
+ if ctor is not None and ctor.parameters:
1078
+ invalid_handle_logic = ""
1079
+ else:
1080
+ invalid_handle_logic = f"""invalid_handle:
1081
+ {{
1082
+ napi_throw_type_error (env, NULL, "expected a {otype.js_name} handle");
1083
+ goto propagate_error;
1084
+ }}
1085
+ """
1086
+
1087
+ custom = otype.customizations
1088
+
1089
+ finalizer = "fdn_object_finalize"
1090
+
1091
+ cleanup_code = ""
1092
+ if custom is not None and custom.cleanup is not None:
1093
+ cleanup_code = f"""
1094
+
1095
+ napi_add_env_cleanup_hook (env, {otype_cprefix}_handle_cleanup, handle);
1096
+ g_object_set_data (G_OBJECT (handle), "fdn-cleanup-hook", {otype_cprefix}_handle_cleanup);"""
1097
+ finalizer = f"{otype_cprefix}_finalize"
1098
+
1099
+ keep_alive_code = ""
1100
+ if custom is not None and custom.keep_alive is not None:
1101
+ keep_alive = custom.keep_alive
1102
+ method = next(
1103
+ (
1104
+ method
1105
+ for method in otype.methods
1106
+ if method.name == keep_alive.is_destroyed_function
1107
+ )
1108
+ )
1109
+ keep_alive_code = f"""
1110
+
1111
+ fdn_keep_alive_until (env, jsthis, G_OBJECT (handle), (FdnIsDestroyedFunc) {method.c_identifier}, "{keep_alive.destroy_signal_name}");"""
1112
+
1113
+ one_newline = "\n"
1114
+ two_newlines = "\n\n"
1115
+
1116
+ return f"""
1117
+ static napi_value
1118
+ {otype_cprefix}_construct (napi_env env,
1119
+ {calculate_indent("_construct")}napi_callback_info info)
1120
+ {{
1121
+ napi_value result = NULL;
1122
+ size_t argc = {n_parameters};
1123
+ napi_value args[{n_parameters}], jsthis;
1124
+ bool is_instance;{indent_c_code(param_declarations, 1, prologue=one_newline)}
1125
+ {otype.c_type} * handle = NULL;
1126
+
1127
+ if (napi_get_cb_info (env, info, &argc, args, &jsthis, NULL) != napi_ok)
1128
+ goto propagate_error;
1129
+
1130
+ if (argc != 0 && napi_check_object_type_tag (env, args[0], &fdn_handle_wrapper_type_tag, &is_instance) == napi_ok && is_instance)
1131
+ {{
1132
+ if (napi_get_value_external (env, args[0], (void **) &handle) != napi_ok)
1133
+ goto propagate_error;
1134
+
1135
+ g_object_ref (handle);
1136
+ }}
1137
+ else
1138
+ {{
1139
+ {indent_c_code(param_conversions, 2)}{indent_c_code(constructor_call, 2, prologue=two_newlines)}{indent_c_code(error_check, 2, prologue=one_newline)}
1140
+ }}
1141
+
1142
+ if (!fdn_object_wrap (env, jsthis, G_OBJECT (handle), {finalizer}))
1143
+ goto propagate_error;{cleanup_code}{keep_alive_code}
1144
+
1145
+ result = jsthis;
1146
+ goto beach;
1147
+
1148
+ {unconstructable_logic}{invalid_handle_logic}{construction_failed_logic}propagate_error:
1149
+ {{
1150
+ g_clear_object (&handle);
1151
+ goto beach;
1152
+ }}
1153
+ beach:
1154
+ {{{indent_c_code(param_destructions, 2, prologue=one_newline)}
1155
+ return result;
1156
+ }}
1157
+ }}
1158
+ """
1159
+
1160
+
1161
+ def generate_object_type_finalizer(otype: ObjectType) -> str:
1162
+ custom = otype.customizations
1163
+ if custom is None or custom.cleanup is None:
1164
+ return ""
1165
+
1166
+ otype_cprefix = otype.c_symbol_prefix
1167
+
1168
+ indent = " " * (len(otype_cprefix) + len("_finalize") + 2)
1169
+
1170
+ return f"""
1171
+ static void
1172
+ {otype_cprefix}_finalize (napi_env env,
1173
+ {indent}void * finalize_data,
1174
+ {indent}void * finalize_hint)
1175
+ {{
1176
+ {otype.c_type} * self = finalize_data;
1177
+
1178
+ if (g_object_steal_data (G_OBJECT (self), "fdn-cleanup-hook") != NULL)
1179
+ napi_remove_env_cleanup_hook (env, {otype_cprefix}_handle_cleanup, self);
1180
+
1181
+ fdn_object_finalize (env, finalize_data, finalize_hint);
1182
+ }}
1183
+ """
1184
+
1185
+
1186
+ def generate_object_type_cleanup_code(otype: ObjectType) -> str:
1187
+ custom = otype.customizations
1188
+ if custom is None or custom.cleanup is None:
1189
+ return ""
1190
+
1191
+ cleanup_method = next(
1192
+ (method for method in otype.methods if method.name == custom.cleanup)
1193
+ )
1194
+
1195
+ return f"""
1196
+ static void
1197
+ {otype.c_symbol_prefix}_handle_cleanup (void * data)
1198
+ {{
1199
+ {otype.c_type} * self = data;
1200
+
1201
+ g_object_steal_data (G_OBJECT (self), "fdn-cleanup-hook");
1202
+
1203
+ {cleanup_method.c_identifier}_sync (self, NULL, NULL);
1204
+ }}
1205
+ """
1206
+
1207
+
1208
+ def generate_method_code(method: Method) -> str:
1209
+ otype = method.object_type
1210
+ operation_type_name = method.operation_type_name
1211
+ otype_cprefix = otype.c_symbol_prefix
1212
+
1213
+ storage_prefix = "operation->" if method.is_async else ""
1214
+ invalid_arg_label = "invalid_argument" if method.is_async else "beach"
1215
+
1216
+ if method.input_parameters:
1217
+ args_declarations = f"""\
1218
+ size_t argc = {len(method.input_parameters)};
1219
+ napi_value args[{len(method.input_parameters)}];"""
1220
+ get_cb_info_argc_args = "&argc, args"
1221
+ else:
1222
+ args_declarations = ""
1223
+ get_cb_info_argc_args = "NULL, NULL"
1224
+
1225
+ param_conversions = generate_input_parameter_conversions_code(
1226
+ method, storage_prefix, invalid_arg_label
1227
+ )
1228
+ param_destructions = generate_parameter_destructions_code(method, storage_prefix)
1229
+
1230
+ return_assignment = generate_return_assignment_code(method, storage_prefix)
1231
+ return_conversion = generate_return_conversion_code(method, storage_prefix)
1232
+ return_destruction = generate_return_destruction_code(method, storage_prefix)
1233
+
1234
+ keep_alive_code = ""
1235
+ custom = method.customizations
1236
+ if custom is not None:
1237
+ if custom.ref_keep_alive:
1238
+ keep_alive_code = (
1239
+ "\n\nnapi_ref_threadsafe_function (env, fdn_keep_alive_tsfn);"
1240
+ )
1241
+ elif custom.unref_keep_alive:
1242
+ keep_alive_code = (
1243
+ "\n\nnapi_unref_threadsafe_function (env, fdn_keep_alive_tsfn);"
1244
+ )
1245
+
1246
+ def calculate_indent(suffix: str) -> str:
1247
+ return " " * (len(otype_cprefix) + 1 + len(method.name) + len(suffix) + 2)
1248
+
1249
+ one_newline = "\n"
1250
+ two_newlines = "\n\n"
1251
+
1252
+ if method.is_async:
1253
+ operation_free_function = f"""\
1254
+ static void
1255
+ {otype_cprefix}_{method.name}_operation_free ({operation_type_name} * operation)
1256
+ {{{indent_c_code(param_destructions, 1, prologue=one_newline)}{indent_c_code(return_destruction, 1, prologue=one_newline)}
1257
+ g_slice_free ({operation_type_name}, operation);
1258
+ }}"""
1259
+
1260
+ code = f"""
1261
+ static napi_value
1262
+ {otype_cprefix}_{method.name} (napi_env env,
1263
+ {calculate_indent('')}napi_callback_info info)
1264
+ {{{indent_c_code(args_declarations, 1, prologue=one_newline)}
1265
+ napi_value jsthis;
1266
+ {otype.c_type} * handle;
1267
+ napi_deferred deferred;
1268
+ napi_value promise;
1269
+ {operation_type_name} * operation;
1270
+ GSource * source;
1271
+
1272
+ if (napi_get_cb_info (env, info, {get_cb_info_argc_args}, &jsthis, NULL) != napi_ok)
1273
+ return NULL;
1274
+
1275
+ if (napi_unwrap (env, jsthis, (void **) &handle) != napi_ok)
1276
+ return NULL;
1277
+
1278
+ napi_create_promise (env, &deferred, &promise);
1279
+
1280
+ operation = g_slice_new0 ({operation_type_name});
1281
+ operation->deferred = deferred;
1282
+ operation->handle = handle;
1283
+ operation->error = NULL;{indent_c_code(param_conversions, 1, prologue=two_newlines)}
1284
+
1285
+ source = g_idle_source_new ();
1286
+ g_source_set_callback (source, {otype_cprefix}_{method.name}_begin,
1287
+ operation, NULL);
1288
+ g_source_attach (source, frida_get_main_context ());
1289
+ g_source_unref (source);
1290
+
1291
+ napi_ref_threadsafe_function (env, {otype_cprefix}_{method.name}_tsfn);
1292
+
1293
+ return promise;
1294
+
1295
+ invalid_argument:
1296
+ {{
1297
+ napi_reject_deferred (env, deferred, NULL);
1298
+ {otype_cprefix}_{method.name}_operation_free (operation);
1299
+ return NULL;
1300
+ }}
1301
+ }}
1302
+
1303
+ static gboolean
1304
+ {otype_cprefix}_{method.name}_begin (gpointer user_data)
1305
+ {{
1306
+ {operation_type_name} * operation = user_data;
1307
+
1308
+ {method.c_identifier} (operation->handle,
1309
+ {", ".join([f"operation->{param.name}" for param in method.parameters])},
1310
+ {otype_cprefix}_{method.name}_end, operation);
1311
+
1312
+ return G_SOURCE_REMOVE;
1313
+ }}
1314
+
1315
+ static void
1316
+ {otype_cprefix}_{method.name}_end (GObject * source_object,
1317
+ {calculate_indent("_end")}GAsyncResult * res,
1318
+ {calculate_indent("_end")}gpointer user_data)
1319
+ {{
1320
+ {operation_type_name} * operation = user_data;
1321
+
1322
+ {return_assignment}{method.finish_c_identifier} (operation->handle, res, &operation->error);
1323
+
1324
+ napi_call_threadsafe_function ({otype_cprefix}_{method.name}_tsfn, operation, napi_tsfn_blocking);
1325
+ }}
1326
+
1327
+ static void
1328
+ {otype_cprefix}_{method.name}_deliver (napi_env env,
1329
+ {calculate_indent("_deliver")}napi_value js_cb,
1330
+ {calculate_indent("_deliver")}void * context,
1331
+ {calculate_indent("_deliver")}void * data)
1332
+ {{
1333
+ {operation_type_name} * operation = data;
1334
+
1335
+ if (operation->error != NULL)
1336
+ {{
1337
+ napi_value error_obj = fdn_error_to_value (env, operation->error);
1338
+ napi_reject_deferred (env, operation->deferred, error_obj);
1339
+ g_error_free (operation->error);
1340
+ }}
1341
+ else
1342
+ {{
1343
+ napi_value js_retval;
1344
+ {indent_c_code(return_conversion, 2)}
1345
+ napi_resolve_deferred (env, operation->deferred, js_retval);{indent_c_code(keep_alive_code, 2)}
1346
+ }}
1347
+
1348
+ {otype_cprefix}_{method.name}_operation_free (operation);
1349
+
1350
+ napi_unref_threadsafe_function (env, {otype_cprefix}_{method.name}_tsfn);
1351
+ }}
1352
+
1353
+ {operation_free_function}
1354
+ """
1355
+ else:
1356
+ param_declarations = generate_parameter_variable_declarations(
1357
+ method, initialize=True
1358
+ )
1359
+ return_declaration = generate_return_variable_declaration(method)
1360
+ call_args = generate_call_arguments_code(
1361
+ method, storage_prefix, instance_arg="handle"
1362
+ )
1363
+
1364
+ if method.throws:
1365
+ error_check = """if (error != NULL)
1366
+ goto call_failed;"""
1367
+ call_failed_logic = """call_failed:
1368
+ {
1369
+ napi_throw (env, fdn_error_to_value (env, error));
1370
+ g_error_free (error);
1371
+ goto beach;
1372
+ }
1373
+ """
1374
+ else:
1375
+ error_check = ""
1376
+ call_failed_logic = ""
1377
+
1378
+ post_call_logic = "".join(
1379
+ [
1380
+ indent_c_code(error_check, 1, prologue=one_newline),
1381
+ indent_c_code(keep_alive_code, 1, prologue=two_newlines),
1382
+ indent_c_code(return_conversion, 1, prologue=two_newlines),
1383
+ indent_c_code(return_destruction, 1, prologue=one_newline),
1384
+ ]
1385
+ )
1386
+
1387
+ code = f"""
1388
+ static napi_value
1389
+ {otype_cprefix}_{method.name} (napi_env env,
1390
+ {calculate_indent('')}napi_callback_info info)
1391
+ {{
1392
+ napi_value js_retval = NULL;{indent_c_code(args_declarations, 1, prologue=one_newline)}
1393
+ napi_value jsthis;
1394
+ {otype.c_type} * handle;{indent_c_code(param_declarations, 1, prologue=one_newline)}{indent_c_code(return_declaration, 1, prologue=one_newline)}
1395
+
1396
+ if (napi_get_cb_info (env, info, {get_cb_info_argc_args}, &jsthis, NULL) != napi_ok)
1397
+ goto beach;
1398
+
1399
+ if (napi_unwrap (env, jsthis, (void **) &handle) != napi_ok)
1400
+ goto beach;{indent_c_code(param_conversions, 1, prologue=two_newlines)}
1401
+
1402
+ {return_assignment}{method.c_identifier} ({call_args});{post_call_logic}
1403
+ goto beach;
1404
+
1405
+ {call_failed_logic}beach:
1406
+ {{{indent_c_code(param_destructions, 2, prologue=one_newline)}
1407
+ return js_retval;
1408
+ }}
1409
+ }}
1410
+ """
1411
+ return code
1412
+
1413
+
1414
+ def generate_parameter_variable_declarations(
1415
+ proc: Procedure, initialize: bool = False
1416
+ ) -> str:
1417
+ decls = []
1418
+
1419
+ for param in proc.parameters:
1420
+ line = f"{param.type.c.replace('const ', '')} {param.name}"
1421
+ if initialize:
1422
+ default_val = param.type.default_value
1423
+ if default_val is not None:
1424
+ line += f" = {default_val}"
1425
+ line += ";"
1426
+ decls.append(line)
1427
+
1428
+ if proc.throws:
1429
+ line = "GError * error"
1430
+ if initialize:
1431
+ line += " = NULL"
1432
+ line += ";"
1433
+ decls.append(line)
1434
+
1435
+ return "\n".join(decls)
1436
+
1437
+
1438
+ def generate_input_parameter_conversions_code(
1439
+ proc: Procedure, storage_prefix: str, invalid_arg_label: str
1440
+ ) -> str:
1441
+ conversions = [
1442
+ generate_parameter_conversion_code(param, i, storage_prefix, invalid_arg_label)
1443
+ for i, param in enumerate(proc.input_parameters)
1444
+ ]
1445
+ return "\n\n".join(conversions)
1446
+
1447
+
1448
+ def generate_parameter_destructions_code(proc: Procedure, storage_prefix: str) -> str:
1449
+ destructions = [
1450
+ generate_parameter_destruction_code(param, storage_prefix)
1451
+ for param in proc.parameters
1452
+ ]
1453
+ return "\n".join([d for d in destructions if d is not None])
1454
+
1455
+
1456
+ def generate_parameter_conversion_code(
1457
+ param: Parameter, index: int, storage_prefix: str, invalid_arg_label: str
1458
+ ) -> str:
1459
+ code = f"""\
1460
+ if (argc > {index} && !fdn_is_undefined_or_null (env, args[{index}]))
1461
+ {{
1462
+ if (!fdn_{param.type.nick}_from_value (env, args[{index}], &{storage_prefix}{param.name}))
1463
+ goto {invalid_arg_label};
1464
+ }}
1465
+ else
1466
+ {{
1467
+ """
1468
+
1469
+ if param.nullable:
1470
+ code += f" {storage_prefix}{param.name} = NULL;"
1471
+ else:
1472
+ code += f""" napi_throw_type_error (env, NULL, "missing argument: {param.js_name}");
1473
+ goto {invalid_arg_label};"""
1474
+
1475
+ code += "\n}"
1476
+
1477
+ return code
1478
+
1479
+
1480
+ def generate_parameter_destruction_code(
1481
+ param: Parameter, storage_prefix: str
1482
+ ) -> Optional[str]:
1483
+ func = param.destroy_func
1484
+ if func is None:
1485
+ return None
1486
+ return generate_destruction_code(f"{storage_prefix}{param.name}", func)
1487
+
1488
+
1489
+ def generate_call_arguments_code(
1490
+ proc: Procedure, storage_prefix: str, instance_arg: Optional[str] = None
1491
+ ) -> str:
1492
+ names = []
1493
+ if instance_arg is not None:
1494
+ names.append(instance_arg)
1495
+ names += [f"{storage_prefix}{param.name}" for param in proc.parameters]
1496
+ if proc.throws:
1497
+ names.append(f"&{storage_prefix}error")
1498
+ return ", ".join(names)
1499
+
1500
+
1501
+ def generate_return_variable_declaration(method: Method) -> str:
1502
+ return (
1503
+ f"{method.return_value.type.c} retval;"
1504
+ if method.return_value is not None
1505
+ else ""
1506
+ )
1507
+
1508
+
1509
+ def generate_return_assignment_code(method: Method, storage_prefix: str) -> str:
1510
+ return f"{storage_prefix}retval = " if method.return_value is not None else ""
1511
+
1512
+
1513
+ def generate_return_conversion_code(method: Method, storage_prefix: str) -> str:
1514
+ if method.return_value is not None:
1515
+ custom = method.customizations
1516
+ if custom is not None and custom.return_cconversion is not None:
1517
+ code = f"js_retval = {custom.return_cconversion};"
1518
+ else:
1519
+ code = f"js_retval = fdn_{method.return_value.type.nick}_to_value (env, {storage_prefix}retval);"
1520
+ if method.return_value.nullable:
1521
+ code = f"if ({storage_prefix}retval != NULL)\n {code}\nelse\n napi_get_null (env, &js_retval);"
1522
+ else:
1523
+ code = "napi_get_undefined (env, &js_retval);"
1524
+ return code
1525
+
1526
+
1527
+ def generate_return_destruction_code(method: Method, storage_prefix: str) -> str:
1528
+ retval = method.return_value
1529
+ if retval is None:
1530
+ return ""
1531
+ func = retval.destroy_func
1532
+ if func is None:
1533
+ return ""
1534
+ return generate_destruction_code(f"{storage_prefix}retval", func)
1535
+
1536
+
1537
+ def generate_destruction_code(variable: str, destroy_func: str):
1538
+ if destroy_func == "g_free":
1539
+ return f"g_free ({variable});"
1540
+ return f"g_clear_pointer (&{variable}, {destroy_func});"
1541
+
1542
+
1543
+ def generate_signal_getter_code(otype: ObjectType, signal: Signal) -> str:
1544
+ cprefix = otype.c_symbol_prefix
1545
+
1546
+ custom = signal.customizations
1547
+ behavior = custom.behavior if custom is not None else "FDN_SIGNAL_ALLOW_EXIT"
1548
+
1549
+ indent = " " * (len(cprefix) + 5 + len(signal.c_name) + 9)
1550
+
1551
+ return f"""
1552
+ static napi_value
1553
+ {cprefix}_get_{signal.c_name}_signal (napi_env env,
1554
+ {indent}napi_callback_info info)
1555
+ {{
1556
+ return fdn_object_get_signal (env, info, "{signal.name}", "_{signal.prefixed_js_name}", {behavior});
1557
+ }}
1558
+ """
1559
+
1560
+
1561
+ def generate_abstract_base_type_declarations(model: Model) -> str:
1562
+ decls = []
1563
+ for itype in model.interface_types_with_abstract_base:
1564
+ ctype = itype.abstract_base_c_type
1565
+ cprefix = itype.abstract_base_c_symbol_prefix
1566
+ module_upper, obj_name_upper = cprefix.upper().split("_", maxsplit=1)
1567
+ decls.append(
1568
+ f"""G_DECLARE_FINAL_TYPE ({ctype}, {cprefix}, {module_upper}, {obj_name_upper}, GObject)
1569
+
1570
+ struct _{ctype}
1571
+ {{
1572
+ GObject parent;
1573
+
1574
+ napi_ref wrapper;
1575
+ gint disposed;
1576
+ }};
1577
+ """
1578
+ )
1579
+ return "\n".join(decls) + "\n"
1580
+
1581
+
1582
+ def generate_abstract_base_define_type_invocations(model: Model) -> str:
1583
+ invocations = []
1584
+ for itype in model.interface_types_with_abstract_base:
1585
+ cprefix = itype.abstract_base_c_symbol_prefix
1586
+ invocations.append(
1587
+ f"""G_DEFINE_TYPE_EXTENDED ({itype.abstract_base_c_type},
1588
+ {cprefix},
1589
+ G_TYPE_OBJECT,
1590
+ 0,
1591
+ G_IMPLEMENT_INTERFACE ({itype.get_type} (),
1592
+ {cprefix}_iface_init))"""
1593
+ )
1594
+ return "\n\n".join(invocations) + "\n\n"
1595
+
1596
+
1597
+ def generate_abstract_base_registration_code(otype: ObjectType) -> str:
1598
+ otype_cprefix = otype.abstract_base_c_symbol_prefix
1599
+
1600
+ tsfn_initializations = [
1601
+ f"""\
1602
+ napi_create_threadsafe_function (env, NULL, NULL, fdn_utf8_to_value (env, "cleanup"), 0, 1, NULL, NULL, NULL, {otype_cprefix}_release_js_resources, &{otype_cprefix}_release_js_resources_tsfn);
1603
+ napi_unref_threadsafe_function (env, {otype_cprefix}_release_js_resources_tsfn);"""
1604
+ ]
1605
+
1606
+ for method in otype.methods:
1607
+ if method.is_property_accessor:
1608
+ continue
1609
+ tsfn_initializations.append(
1610
+ f"""\
1611
+ napi_create_threadsafe_function (env, NULL, NULL, fdn_utf8_to_value (env, "{method.prefixed_js_name}"), 0, 1, NULL, NULL, NULL, {otype_cprefix}_{method.name}_begin, &{otype_cprefix}_{method.name}_tsfn);
1612
+ napi_unref_threadsafe_function (env, {otype_cprefix}_{method.name}_tsfn);"""
1613
+ )
1614
+
1615
+ tsfn_initializations_code = "\n\n".join(tsfn_initializations)
1616
+
1617
+ def calculate_indent(suffix: str) -> str:
1618
+ return " " * (len(otype_cprefix) + len(suffix) + 2)
1619
+
1620
+ two_newlines = "\n\n"
1621
+
1622
+ return f"""
1623
+ static void
1624
+ {otype_cprefix}_register (napi_env env,
1625
+ {calculate_indent("_register")}napi_value exports)
1626
+ {{
1627
+ napi_value constructor;
1628
+ napi_define_class (env, "Abstract{otype.js_name}", NAPI_AUTO_LENGTH, {otype_cprefix}_construct, NULL, 0, NULL, &constructor);
1629
+
1630
+ napi_set_named_property (env, exports, "Abstract{otype.js_name}", constructor);{indent_c_code(tsfn_initializations_code, 1, prologue=two_newlines)}
1631
+ }}
1632
+ """
1633
+
1634
+
1635
+ def generate_abstract_base_constructor(otype: ObjectType) -> str:
1636
+ otype_cprefix = otype.abstract_base_c_symbol_prefix
1637
+
1638
+ def calculate_indent(suffix: str) -> str:
1639
+ return " " * (len(otype_cprefix) + len(suffix) + 2)
1640
+
1641
+ return f"""
1642
+ static napi_value
1643
+ {otype_cprefix}_construct (napi_env env,
1644
+ {calculate_indent("_construct")}napi_callback_info info)
1645
+ {{
1646
+ napi_value jsthis;
1647
+ {otype.abstract_base_c_type} * handle = NULL;
1648
+
1649
+ if (napi_get_cb_info (env, info, NULL, NULL, &jsthis, NULL) != napi_ok)
1650
+ goto propagate_error;
1651
+
1652
+ handle = g_object_new ({otype_cprefix}_get_type (), NULL);
1653
+
1654
+ if (!fdn_object_wrap (env, jsthis, G_OBJECT (handle), fdn_object_finalize))
1655
+ goto propagate_error;
1656
+
1657
+ napi_create_reference (env, jsthis, 1, &handle->wrapper);
1658
+
1659
+ return jsthis;
1660
+
1661
+ propagate_error:
1662
+ {{
1663
+ g_clear_object (&handle);
1664
+ return NULL;
1665
+ }}
1666
+ }}
1667
+ """
1668
+
1669
+
1670
+ def generate_abstract_base_gobject_glue(otype: ObjectType) -> str:
1671
+ otype_cprefix = otype.abstract_base_c_symbol_prefix
1672
+ ctype = otype.abstract_base_c_type
1673
+
1674
+ vmethod_lines = []
1675
+ for m in otype.methods:
1676
+ vmethod_lines += [
1677
+ f"iface->{m.name} = {otype_cprefix}_{m.name};",
1678
+ f"iface->{m.name}_finish = {otype_cprefix}_{m.name}_finish;",
1679
+ ]
1680
+ vmethod_assignments = indent_c_code("\n".join(vmethod_lines), 1)
1681
+
1682
+ def calculate_indent(suffix: str) -> str:
1683
+ return " " * (len(otype_cprefix) + len(suffix) + 2)
1684
+
1685
+ return f"""
1686
+ static void
1687
+ {otype_cprefix}_class_init ({ctype}Class * klass)
1688
+ {{
1689
+ GObjectClass * object_class = G_OBJECT_CLASS (klass);
1690
+
1691
+ object_class->dispose = {otype_cprefix}_dispose;
1692
+ }}
1693
+
1694
+ static void
1695
+ {otype_cprefix}_iface_init (gpointer g_iface,
1696
+ {calculate_indent("_iface_init")}gpointer iface_data)
1697
+ {{
1698
+ {otype.type_struct} * iface = g_iface;
1699
+
1700
+ {vmethod_assignments}
1701
+ }}
1702
+
1703
+ static void
1704
+ {otype_cprefix}_init ({ctype} * self)
1705
+ {{
1706
+ self->disposed = FALSE;
1707
+ }}
1708
+
1709
+ static void
1710
+ {otype_cprefix}_dispose (GObject * object)
1711
+ {{
1712
+ {otype.abstract_base_c_type} * self = {otype.abstract_base_c_cast_macro} (object);
1713
+
1714
+ if (g_atomic_int_compare_and_exchange (&self->disposed, FALSE, TRUE) && !fdn_in_cleanup)
1715
+ {{
1716
+ napi_call_threadsafe_function ({otype_cprefix}_release_js_resources_tsfn, g_object_ref (self), napi_tsfn_blocking);
1717
+ }}
1718
+
1719
+ G_OBJECT_CLASS ({otype_cprefix}_parent_class)->dispose (object);
1720
+ }}
1721
+
1722
+ static void
1723
+ {otype_cprefix}_release_js_resources (napi_env env,
1724
+ {calculate_indent("_release_js_resources")}napi_value js_cb,
1725
+ {calculate_indent("_release_js_resources")}void * context,
1726
+ {calculate_indent("_release_js_resources")}void * data)
1727
+ {{
1728
+ {otype.abstract_base_c_type} * self = data;
1729
+
1730
+ napi_delete_reference (env, self->wrapper);
1731
+ self->wrapper = NULL;
1732
+
1733
+ g_object_unref (self);
1734
+ }}
1735
+ """
1736
+
1737
+
1738
+ def generate_abstract_base_method_code(method: Method) -> str:
1739
+ otype = method.object_type
1740
+ operation_type_name = method.abstract_base_operation_type_name
1741
+ otype_cprefix = otype.abstract_base_c_symbol_prefix
1742
+
1743
+ method_name_pascal = to_pascal_case(method.name)
1744
+ method_cprefix = f"{otype_cprefix}_{method.name}"
1745
+
1746
+ def calculate_indent(suffix: str) -> str:
1747
+ return " " * (len(otype_cprefix) + len(method.name) + len(suffix) + 3)
1748
+
1749
+ params = f",\n{calculate_indent('')}".join(method.param_ctypings)
1750
+ finish_params = f",\n{calculate_indent('_finish')}".join(
1751
+ method.finish_param_ctypings
1752
+ )
1753
+
1754
+ storage_prefix = "operation->"
1755
+
1756
+ param_assignments = generate_abstract_base_input_parameter_assignment_code(
1757
+ method, storage_prefix
1758
+ )
1759
+ param_conversions = generate_abstract_base_input_parameter_conversions_code(
1760
+ method, storage_prefix
1761
+ )
1762
+ param_destructions = generate_parameter_destructions_code(method, storage_prefix)
1763
+ cancellable_name = next(
1764
+ (p.name for p in method.parameters if p.type.name == "Gio.Cancellable"), "NULL"
1765
+ )
1766
+
1767
+ result_declaration = generate_return_variable_declaration(method)
1768
+ result_conversion, result_destroy = generate_abstract_base_return_conversion_code(
1769
+ method, "propagate_error"
1770
+ )
1771
+
1772
+ finish_error = "error" if method.throws else "NULL"
1773
+ finish_statement = f"g_task_propagate_pointer (G_TASK (result), {finish_error})"
1774
+ retval = method.return_value
1775
+ if retval is not None:
1776
+ from_pointer_func = retval.type.from_pointer_func
1777
+ if from_pointer_func is not None:
1778
+ finish_statement = f"{from_pointer_func} ({finish_statement})"
1779
+ finish_statement = f"return {finish_statement}"
1780
+ finish_code = f"{finish_statement};"
1781
+
1782
+ one_newline = "\n"
1783
+ two_newlines = "\n\n"
1784
+
1785
+ operation_free_function = f"""\
1786
+ static void
1787
+ {method_cprefix}_operation_free ({operation_type_name} * operation)
1788
+ {{{indent_c_code(param_destructions, 1, prologue=one_newline)}
1789
+ g_slice_free ({operation_type_name}, operation);
1790
+ }}"""
1791
+
1792
+ return f"""
1793
+ static void
1794
+ {method_cprefix} ({params})
1795
+ {{
1796
+ {otype.abstract_base_c_type} * self;
1797
+ {operation_type_name} * operation;
1798
+ GTask * task;
1799
+
1800
+ self = {otype.abstract_base_c_cast_macro} ({method.cself_name});
1801
+
1802
+ operation = g_slice_new0 ({operation_type_name});
1803
+ operation->self = self;{indent_c_code(param_assignments, 1, prologue=one_newline)}
1804
+
1805
+ task = g_task_new (self, {cancellable_name}, callback, user_data);
1806
+ g_task_set_task_data (task, operation, (GDestroyNotify) {otype_cprefix}_{method.name}_operation_free);
1807
+
1808
+ napi_call_threadsafe_function ({otype_cprefix}_{method.name}_tsfn, task, napi_tsfn_blocking);
1809
+ }}
1810
+
1811
+ {operation_free_function}
1812
+
1813
+ static void
1814
+ {method_cprefix}_begin (napi_env env,
1815
+ {calculate_indent("_begin")}napi_value js_cb,
1816
+ {calculate_indent("_begin")}void * context,
1817
+ {calculate_indent("_begin")}void * data)
1818
+ {{
1819
+ GTask * task = data;
1820
+ {operation_type_name} * operation;
1821
+ {otype.abstract_base_c_type} * self;
1822
+ napi_value wrapper, method, args[{len(method.input_parameters)}], js_retval, then, then_args[2], then_retval;
1823
+
1824
+ operation = g_task_get_task_data (task);
1825
+ self = operation->self;
1826
+
1827
+ if (napi_get_reference_value (env, self->wrapper, &wrapper) != napi_ok)
1828
+ goto propagate_error;
1829
+
1830
+ if (napi_get_named_property (env, wrapper, "{method.prefixed_js_name}", &method) != napi_ok)
1831
+ goto propagate_error;{indent_c_code(param_conversions, 1, prologue=two_newlines)}
1832
+
1833
+ if (napi_call_function (env, wrapper, method, G_N_ELEMENTS (args), args, &js_retval) != napi_ok)
1834
+ goto propagate_error;
1835
+
1836
+ if (napi_get_named_property (env, js_retval, "then", &then) != napi_ok)
1837
+ goto propagate_error;
1838
+
1839
+ napi_create_function (env, "on{method_name_pascal}Success", NAPI_AUTO_LENGTH, {otype_cprefix}_{method.name}_on_success, task, &then_args[0]);
1840
+ napi_create_function (env, "on{method_name_pascal}Failure", NAPI_AUTO_LENGTH, {otype_cprefix}_{method.name}_on_failure, task, &then_args[1]);
1841
+
1842
+ if (napi_call_function (env, js_retval, then, G_N_ELEMENTS (then_args), then_args, &then_retval) != napi_ok)
1843
+ goto propagate_error;
1844
+
1845
+ return;
1846
+
1847
+ propagate_error:
1848
+ {{
1849
+ napi_value js_error;
1850
+ GError * error;
1851
+
1852
+ napi_get_and_clear_last_exception (env, &js_error);
1853
+ fdn_error_from_value (env, js_error, &error);
1854
+
1855
+ g_task_return_error (task, error);
1856
+ g_object_unref (task);
1857
+
1858
+ return;
1859
+ }}
1860
+ }}
1861
+
1862
+ static napi_value
1863
+ {method_cprefix}_on_success (napi_env env,
1864
+ {calculate_indent("_on_success")}napi_callback_info info)
1865
+ {{
1866
+ size_t argc = 1;
1867
+ napi_value js_retval;
1868
+ GTask * task;{indent_c_code(result_declaration, 1, prologue=one_newline)}
1869
+ gpointer raw_result;
1870
+
1871
+ if (napi_get_cb_info (env, info, &argc, &js_retval, NULL, (void **) &task) != napi_ok)
1872
+ goto propagate_error;
1873
+ if (argc != 1)
1874
+ goto internal_error;
1875
+
1876
+ {indent_c_code(result_conversion, 1)}
1877
+
1878
+ g_task_return_pointer (task, raw_result, {result_destroy});
1879
+ goto beach;
1880
+
1881
+ propagate_error:
1882
+ {{
1883
+ napi_value js_error;
1884
+ GError * error;
1885
+
1886
+ napi_get_and_clear_last_exception (env, &js_error);
1887
+ fdn_error_from_value (env, js_error, &error);
1888
+
1889
+ g_task_return_error (task, error);
1890
+
1891
+ goto beach;
1892
+ }}
1893
+ internal_error:
1894
+ {{
1895
+ g_task_return_new_error (task, FRIDA_ERROR, FRIDA_ERROR_INVALID_OPERATION,
1896
+ "Internal error");
1897
+
1898
+ goto beach;
1899
+ }}
1900
+ beach:
1901
+ {{
1902
+ napi_value val;
1903
+
1904
+ g_object_unref (task);
1905
+
1906
+ napi_get_undefined (env, &val);
1907
+ return val;
1908
+ }}
1909
+ }}
1910
+
1911
+ static napi_value
1912
+ {method_cprefix}_on_failure (napi_env env,
1913
+ {calculate_indent("_on_failure")}napi_callback_info info)
1914
+ {{
1915
+ size_t argc = 1;
1916
+ napi_value js_error;
1917
+ GTask * task;
1918
+ GError * error;
1919
+
1920
+ if (napi_get_cb_info (env, info, &argc, &js_error, NULL, (void **) &task) != napi_ok)
1921
+ goto propagate_error;
1922
+ if (argc != 1)
1923
+ goto internal_error;
1924
+
1925
+ if (!fdn_error_from_value (env, js_error, &error))
1926
+ goto propagate_error;
1927
+
1928
+ g_task_return_error (task, error);
1929
+ goto beach;
1930
+
1931
+ propagate_error:
1932
+ {{
1933
+ napi_value js_error;
1934
+ GError * error;
1935
+
1936
+ napi_get_and_clear_last_exception (env, &js_error);
1937
+ fdn_error_from_value (env, js_error, &error);
1938
+
1939
+ g_task_return_error (task, error);
1940
+
1941
+ goto beach;
1942
+ }}
1943
+ internal_error:
1944
+ {{
1945
+ g_task_return_new_error (task, FRIDA_ERROR, FRIDA_ERROR_INVALID_OPERATION,
1946
+ "Internal error");
1947
+
1948
+ goto beach;
1949
+ }}
1950
+ beach:
1951
+ {{
1952
+ napi_value val;
1953
+
1954
+ g_object_unref (task);
1955
+
1956
+ napi_get_undefined (env, &val);
1957
+ return val;
1958
+ }}
1959
+ }}
1960
+
1961
+ static {method.return_ctyping}
1962
+ {method_cprefix}_finish ({finish_params})
1963
+ {{
1964
+ {finish_code}
1965
+ }}
1966
+ """
1967
+
1968
+
1969
+ def generate_abstract_base_input_parameter_assignment_code(
1970
+ proc: Procedure, storage_prefix: str
1971
+ ) -> str:
1972
+ assigments = [
1973
+ generate_abstract_base_parameter_assignment_code(param, i, storage_prefix)
1974
+ for i, param in enumerate(proc.input_parameters)
1975
+ ]
1976
+ return "\n".join(assigments)
1977
+
1978
+
1979
+ def generate_abstract_base_input_parameter_conversions_code(
1980
+ proc: Procedure, storage_prefix: str
1981
+ ) -> str:
1982
+ conversions = [
1983
+ generate_abstract_base_parameter_conversion_code(param, i, storage_prefix)
1984
+ for i, param in enumerate(proc.input_parameters)
1985
+ ]
1986
+ return "\n\n".join(conversions)
1987
+
1988
+
1989
+ def generate_abstract_base_parameter_conversion_code(
1990
+ param: Parameter, index: int, storage_prefix: str
1991
+ ) -> str:
1992
+ lval = f"{storage_prefix}{param.name}"
1993
+
1994
+ code = f"args[{index}] = fdn_{param.type.nick}_to_value (env, {lval});"
1995
+ if param.nullable:
1996
+ code = f"if ({lval} != NULL)\n {code}\nelse\n napi_get_null (env, &args[{index}]);"
1997
+
1998
+ return code
1999
+
2000
+
2001
+ def generate_abstract_base_parameter_assignment_code(
2002
+ param: Parameter, index: int, storage_prefix: str
2003
+ ) -> str:
2004
+ lval = f"{storage_prefix}{param.name}"
2005
+
2006
+ copy_func = param.copy_func
2007
+ if copy_func is not None:
2008
+ if param.nullable and copy_func not in {"g_strdup", "g_strdupv"}:
2009
+ return (
2010
+ f"{lval} = ({param.name} != NULL) ? {copy_func} ({param.name}) : NULL;"
2011
+ )
2012
+ return f"{lval} = {copy_func} ({param.name});"
2013
+
2014
+ return f"{lval} = {param.name};"
2015
+
2016
+
2017
+ def generate_abstract_base_return_conversion_code(
2018
+ method: Method, invalid_label: str
2019
+ ) -> Tuple[str, str]:
2020
+ retval = method.return_value
2021
+ if retval is not None:
2022
+ destroy_func = retval.destroy_func
2023
+ if destroy_func is None:
2024
+ destroy_func = "NULL"
2025
+
2026
+ result_conversion = (
2027
+ f"{retval.type.to_pointer_func} (retval)"
2028
+ if retval.type.to_pointer_func is not None
2029
+ else "retval"
2030
+ )
2031
+
2032
+ code = f"""\
2033
+ if (!fdn_{retval.type.nick}_from_value (env, js_retval, &retval))
2034
+ goto {invalid_label};
2035
+
2036
+ raw_result = {result_conversion};"""
2037
+
2038
+ if retval.nullable:
2039
+ code = f"if (!fdn_is_null (js_result))\n{{ {indent_c_code(code, 1)}\n}} else {{\n raw_result = NULL;\n }}"
2040
+
2041
+ else:
2042
+ code = "raw_result = NULL;"
2043
+ destroy_func = "NULL"
2044
+
2045
+ return (code, destroy_func)
2046
+
2047
+
2048
+ def generate_enum_registration_code(enum: Enumeration) -> str:
2049
+ cprefix = enum.c_symbol_prefix
2050
+
2051
+ properties = []
2052
+ for member in enum.members:
2053
+ properties.append(
2054
+ f'{{ "{member.js_name}", NULL, NULL, NULL, NULL, fdn_utf8_to_value (env, "{member.nick}"), napi_enumerable, NULL }}'
2055
+ )
2056
+
2057
+ properties_str = ",\n ".join(properties)
2058
+
2059
+ def calculate_indent(suffix: str) -> str:
2060
+ return " " * (len(cprefix) + len(suffix) + 2)
2061
+
2062
+ return f"""
2063
+ static void
2064
+ {cprefix}_register (napi_env env,
2065
+ {calculate_indent("_register")}napi_value exports)
2066
+ {{
2067
+ napi_value enum_object;
2068
+ napi_property_descriptor properties[] = {{
2069
+ {properties_str}
2070
+ }};
2071
+
2072
+ napi_create_object (env, &enum_object);
2073
+ napi_define_properties (env, enum_object, G_N_ELEMENTS (properties), properties);
2074
+
2075
+ napi_set_named_property (env, exports, "{enum.js_name}", enum_object);
2076
+ }}
2077
+ """
2078
+
2079
+
2080
+ def generate_enum_conversion_functions(enum: Enumeration) -> str:
2081
+ cprefix = enum.c_symbol_prefix
2082
+
2083
+ def calculate_indent(suffix: str) -> str:
2084
+ return " " * (len(cprefix) + len(suffix) + 2)
2085
+
2086
+ return f"""
2087
+ static gboolean
2088
+ {cprefix}_from_value (napi_env env,
2089
+ {calculate_indent("_from_value")}napi_value value,
2090
+ {calculate_indent("_from_value")}{enum.c_type} * e)
2091
+ {{
2092
+ return fdn_enum_from_value (env, {enum.get_type} (), value, (gint *) e);
2093
+ }}
2094
+
2095
+ static napi_value
2096
+ {cprefix}_to_value (napi_env env,
2097
+ {calculate_indent("_to_value")}{enum.c_type} e)
2098
+ {{
2099
+ return fdn_enum_to_value (env, {enum.get_type} (), e);
2100
+ }}
2101
+ """
2102
+
2103
+
2104
+ def generate_options_conversion_functions(otype: ObjectType) -> str:
2105
+ cprefix = otype.c_symbol_prefix
2106
+
2107
+ def calculate_indent(suffix: str) -> str:
2108
+ return " " * (len(cprefix) + len(suffix) + 2)
2109
+
2110
+ selection_code = ""
2111
+ for method in otype.methods:
2112
+ if not method.is_select_method:
2113
+ continue
2114
+
2115
+ plural_noun = method.select_plural_noun
2116
+ param_type = method.select_element_type
2117
+ param_from_value = f"fdn_{param_type.nick}_from_value"
2118
+
2119
+ element_destroy_code = ""
2120
+ destroy_func = param_type.destroy_func
2121
+ if destroy_func is not None:
2122
+ element_destroy_code = f"\n\n {destroy_func} (element);"
2123
+
2124
+ selection_code += f"""
2125
+
2126
+ {{
2127
+ napi_value js_{plural_noun};
2128
+ napi_valuetype value_type;
2129
+
2130
+ if (napi_get_named_property (env, value, "{plural_noun}", &js_{plural_noun}) != napi_ok)
2131
+ goto propagate_error;
2132
+
2133
+ if (napi_typeof (env, js_{plural_noun}, &value_type) != napi_ok)
2134
+ goto propagate_error;
2135
+
2136
+ if (value_type != napi_undefined)
2137
+ {{
2138
+ uint32_t length, i;
2139
+
2140
+ if (napi_get_array_length (env, js_{plural_noun}, &length) != napi_ok)
2141
+ goto propagate_error;
2142
+
2143
+ for (i = 0; i != length; i++)
2144
+ {{
2145
+ napi_value js_element;
2146
+ {param_type.c.replace('const ', '')} element;
2147
+
2148
+ if (napi_get_element (env, js_{plural_noun}, i, &js_element) != napi_ok)
2149
+ goto propagate_error;
2150
+
2151
+ if (!{param_from_value} (env, js_element, &element))
2152
+ goto propagate_error;
2153
+
2154
+ {method.c_identifier} (opts, element);{element_destroy_code}
2155
+ }}
2156
+ }}
2157
+ }}"""
2158
+
2159
+ cleanup_code = ""
2160
+ if selection_code:
2161
+ cleanup_code += """
2162
+
2163
+ propagate_error:
2164
+ {
2165
+ g_object_unref (opts);
2166
+ return FALSE;
2167
+ }"""
2168
+
2169
+ return f"""
2170
+ static gboolean
2171
+ {cprefix}_from_value (napi_env env,
2172
+ {calculate_indent("_from_value")}napi_value value,
2173
+ {calculate_indent("_from_value")}{otype.c_type} ** options)
2174
+ {{
2175
+ {otype.c_type} * opts;
2176
+
2177
+ if (!fdn_options_from_value (env, {otype.get_type} (), value, (gpointer *) &opts))
2178
+ return FALSE;{selection_code}
2179
+
2180
+ *options = opts;
2181
+ return TRUE;{cleanup_code}
2182
+ }}
2183
+ """
2184
+
2185
+
2186
+ def generate_list_conversion_functions(otype: ObjectType) -> str:
2187
+ cprefix = otype.c_symbol_prefix
2188
+
2189
+ size_method = next((method for method in otype.methods if method.name == "size"))
2190
+ get_method = next((method for method in otype.methods if method.name == "get"))
2191
+
2192
+ element_type = get_method.return_value.type
2193
+
2194
+ def calculate_indent(suffix: str) -> str:
2195
+ return " " * (len(cprefix) + len(suffix) + 2)
2196
+
2197
+ return f"""
2198
+ static napi_value
2199
+ {cprefix}_to_value (napi_env env,
2200
+ {calculate_indent("_to_value")}{otype.c_type} * list)
2201
+ {{
2202
+ napi_value result;
2203
+ gint size, i;
2204
+
2205
+ size = {size_method.c_identifier} (list);
2206
+ napi_create_array_with_length (env, size, &result);
2207
+
2208
+ for (i = 0; i != size; i++)
2209
+ {{
2210
+ {element_type.c} handle = {get_method.c_identifier} (list, i);
2211
+ napi_set_element (env, result, i, fdn_{element_type.nick}_to_value (env, handle));
2212
+ g_object_unref (handle);
2213
+ }}
2214
+
2215
+ return result;
2216
+ }}
2217
+ """
2218
+
2219
+
2220
+ def indent_ts_code(code: str, level: int, prologue: str = "") -> str:
2221
+ prefix = (level * 4) * " "
2222
+ return indent_code(code, prefix, prologue)
2223
+
2224
+
2225
+ def indent_c_code(code: str, level: int, prologue: str = "") -> str:
2226
+ prefix = (level * 2) * " "
2227
+ return indent_code(code, prefix, prologue)
2228
+
2229
+
2230
+ def indent_code(code: str, prefix: str, prologue: str = "") -> str:
2231
+ if not code:
2232
+ return ""
2233
+ return prologue + textwrap.indent(code, prefix, lambda line: line.strip() != "")