frida 16.7.13 → 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.
- package/README.md +1 -10
- package/build/BSDmakefile +6 -0
- package/build/Makefile +10 -0
- package/build/src/frida.d.ts +364 -0
- package/build/src/frida.js +962 -0
- package/build/src/frida_binding.d.ts +938 -0
- package/meson.build +34 -67
- package/package.json +30 -20
- package/scripts/fetch-abi-bits.py +15 -65
- package/scripts/install.js +5 -4
- package/src/addon.def +3 -0
- package/src/addon.symbols +2 -1
- package/src/addon.version +4 -0
- package/src/frida_bindgen/__init__.py +0 -0
- package/src/frida_bindgen/__main__.py +4 -0
- package/src/frida_bindgen/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/frida_bindgen/__pycache__/__main__.cpython-312.pyc +0 -0
- package/src/frida_bindgen/__pycache__/cli.cpython-312.pyc +0 -0
- package/src/frida_bindgen/__pycache__/codegen.cpython-312.pyc +0 -0
- package/src/frida_bindgen/__pycache__/customization.cpython-312.pyc +0 -0
- package/src/frida_bindgen/__pycache__/loader.cpython-312.pyc +0 -0
- package/src/frida_bindgen/__pycache__/model.cpython-312.pyc +0 -0
- package/src/frida_bindgen/assets/codegen_helpers.c +1970 -0
- package/src/frida_bindgen/assets/codegen_helpers.ts +100 -0
- package/src/frida_bindgen/assets/codegen_prototypes.h +78 -0
- package/src/frida_bindgen/assets/codegen_types.h +57 -0
- package/src/frida_bindgen/assets/customization_facade.exports +13 -0
- package/src/frida_bindgen/assets/customization_facade.ts +157 -0
- package/src/frida_bindgen/assets/customization_helpers.imports +2 -0
- package/src/frida_bindgen/assets/customization_helpers.ts +396 -0
- package/src/frida_bindgen/cli.py +96 -0
- package/src/frida_bindgen/codegen.py +2233 -0
- package/src/frida_bindgen/customization.py +924 -0
- package/src/frida_bindgen/loader.py +60 -0
- package/src/frida_bindgen/model.py +1357 -0
- package/src/meson.build +92 -27
- package/{lib/build.py → src/tsc.py} +12 -12
- package/src/win_delay_load_hook.c +56 -0
- package/subprojects/frida-core.wrap +1 -1
- package/test/data/index.ts +2 -2
- package/test/device.ts +1 -2
- package/test/device_manager.ts +1 -2
- package/test/labrat.ts +2 -2
- package/test/script.ts +12 -12
- package/test/session.ts +3 -3
- package/tsconfig.json +6 -11
- package/dist/application.d.ts +0 -81
- package/dist/application.js +0 -2
- package/dist/authentication.d.ts +0 -3
- package/dist/authentication.js +0 -2
- package/dist/bus.d.ts +0 -16
- package/dist/bus.js +0 -23
- package/dist/cancellable.d.ts +0 -15
- package/dist/cancellable.js +0 -41
- package/dist/child.d.ts +0 -16
- package/dist/child.js +0 -9
- package/dist/crash.d.ts +0 -10
- package/dist/crash.js +0 -2
- package/dist/device.d.ts +0 -156
- package/dist/device.js +0 -188
- package/dist/device_manager.d.ts +0 -25
- package/dist/device_manager.js +0 -42
- package/dist/endpoint_parameters.d.ts +0 -26
- package/dist/endpoint_parameters.js +0 -24
- package/dist/icon.d.ts +0 -14
- package/dist/icon.js +0 -2
- package/dist/index.d.ts +0 -161
- package/dist/index.js +0 -170
- package/dist/iostream.d.ts +0 -13
- package/dist/iostream.js +0 -73
- package/dist/native.d.ts +0 -1
- package/dist/native.js +0 -11
- package/dist/portal_membership.d.ts +0 -6
- package/dist/portal_membership.js +0 -12
- package/dist/portal_service.d.ts +0 -48
- package/dist/portal_service.js +0 -52
- package/dist/process.d.ts +0 -47
- package/dist/process.js +0 -2
- package/dist/relay.d.ts +0 -22
- package/dist/relay.js +0 -32
- package/dist/script.d.ts +0 -70
- package/dist/script.js +0 -266
- package/dist/service.d.ts +0 -16
- package/dist/service.js +0 -26
- package/dist/session.d.ts +0 -45
- package/dist/session.js +0 -73
- package/dist/signals.d.ts +0 -20
- package/dist/signals.js +0 -40
- package/dist/socket_address.d.ts +0 -25
- package/dist/socket_address.js +0 -2
- package/dist/spawn.d.ts +0 -4
- package/dist/spawn.js +0 -2
- package/dist/system_parameters.d.ts +0 -84
- package/dist/system_parameters.js +0 -2
- package/lib/application.ts +0 -98
- package/lib/authentication.ts +0 -3
- package/lib/bus.ts +0 -30
- package/lib/cancellable.ts +0 -48
- package/lib/child.ts +0 -15
- package/lib/crash.ts +0 -11
- package/lib/device.ts +0 -331
- package/lib/device_manager.ts +0 -69
- package/lib/endpoint_parameters.ts +0 -56
- package/lib/icon.ts +0 -15
- package/lib/index.ts +0 -316
- package/lib/iostream.ts +0 -78
- package/lib/meson.build +0 -53
- package/lib/native.ts +0 -9
- package/lib/portal_membership.ts +0 -10
- package/lib/portal_service.ts +0 -105
- package/lib/process.ts +0 -57
- package/lib/relay.ts +0 -44
- package/lib/script.ts +0 -361
- package/lib/service.ts +0 -34
- package/lib/session.ts +0 -113
- package/lib/signals.ts +0 -45
- package/lib/socket_address.ts +0 -35
- package/lib/spawn.ts +0 -4
- package/lib/system_parameters.ts +0 -103
- package/meson.options +0 -11
- package/src/addon.cc +0 -78
- package/src/application.cc +0 -148
- package/src/application.h +0 -31
- package/src/authentication.cc +0 -174
- package/src/authentication.h +0 -24
- package/src/bus.cc +0 -167
- package/src/bus.h +0 -33
- package/src/cancellable.cc +0 -117
- package/src/cancellable.h +0 -31
- package/src/child.cc +0 -150
- package/src/child.h +0 -32
- package/src/crash.cc +0 -122
- package/src/crash.h +0 -30
- package/src/device.cc +0 -1350
- package/src/device.h +0 -56
- package/src/device_manager.cc +0 -362
- package/src/device_manager.h +0 -35
- package/src/endpoint_parameters.cc +0 -171
- package/src/endpoint_parameters.h +0 -28
- package/src/glib_context.cc +0 -62
- package/src/glib_context.h +0 -29
- package/src/glib_object.cc +0 -25
- package/src/glib_object.h +0 -37
- package/src/iostream.cc +0 -243
- package/src/iostream.h +0 -30
- package/src/operation.h +0 -94
- package/src/portal_membership.cc +0 -100
- package/src/portal_membership.h +0 -26
- package/src/portal_service.cc +0 -401
- package/src/portal_service.h +0 -40
- package/src/process.cc +0 -135
- package/src/process.h +0 -30
- package/src/relay.cc +0 -139
- package/src/relay.h +0 -31
- package/src/runtime.cc +0 -568
- package/src/runtime.h +0 -69
- package/src/script.cc +0 -301
- package/src/script.h +0 -36
- package/src/service.cc +0 -224
- package/src/service.h +0 -36
- package/src/session.cc +0 -860
- package/src/session.h +0 -42
- package/src/signals.cc +0 -334
- package/src/signals.h +0 -47
- package/src/spawn.cc +0 -95
- package/src/spawn.h +0 -27
- package/src/usage_monitor.h +0 -117
- package/src/uv_context.cc +0 -118
- package/src/uv_context.h +0 -40
- package/src/win_delay_load_hook.cc +0 -63
- package/subprojects/nan.wrap +0 -9
- 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() != "")
|