frida 17.15.5 → 17.16.1

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.
@@ -1,81 +1,20 @@
1
1
  from __future__ import annotations
2
2
 
3
- import xml.etree.ElementTree as ET
4
3
  from collections import OrderedDict, defaultdict
5
4
  from dataclasses import dataclass, field
6
5
  from enum import Enum
7
6
  from functools import cached_property
8
- from typing import (Callable, Iterator, List, Mapping, Optional, Sequence,
9
- Tuple, Union)
10
-
11
- CORE_NAMESPACE = "http://www.gtk.org/introspection/core/1.0"
12
- C_NAMESPACE = "http://www.gtk.org/introspection/c/1.0"
13
- GLIB_NAMESPACE = "http://www.gtk.org/introspection/glib/1.0"
14
- GIR_NAMESPACES = {"": CORE_NAMESPACE, "glib": GLIB_NAMESPACE}
15
-
16
- CORE_TAG_PREFIX = f"{{{CORE_NAMESPACE}}}"
17
-
18
- NUMERIC_GIR_TYPES = {
19
- "gsize",
20
- "gssize",
21
- "gint",
22
- "guint",
23
- "glong",
24
- "gulong",
25
- "gint8",
26
- "gint16",
27
- "gint32",
28
- "gint64",
29
- "guint8",
30
- "guint16",
31
- "guint32",
32
- "guint64",
33
- "gfloat",
34
- "gdouble",
35
- "GType",
36
- "GQuark",
37
- }
38
-
39
- PRIMITIVE_GIR_TYPES = NUMERIC_GIR_TYPES | {
40
- "gpointer",
41
- "gboolean",
42
- "gchar",
43
- "utf8",
44
- "utf8[]",
45
- }
46
-
47
- ResolveTypeCallback = Callable[[str], Tuple[str, ET.Element]]
7
+ from typing import List, Mapping, Optional, Tuple, Union
48
8
 
49
-
50
- @dataclass
51
- class Customizations:
52
- custom_types: Mapping[str, CustomType] = field(default_factory=OrderedDict)
53
- type_customizations: Mapping[str, TypeCustomizations] = field(
54
- default_factory=OrderedDict
55
- )
56
- facade_exports: List[str] = field(default_factory=list)
57
- facade_code: str = ""
58
- helper_imports: List[str] = field(default_factory=list)
59
- helper_code: str = ""
9
+ import frida_bindgen_core as core
10
+ from frida_bindgen_core import (Direction, Namespace, Procedure,
11
+ TransferOwnership, Type)
12
+ from frida_bindgen_core.model import NUMERIC_GIR_TYPES
13
+ from frida_bindgen_core.naming import (to_camel_case, to_macro_case,
14
+ to_pascal_case, to_snake_case)
60
15
 
61
16
 
62
- @dataclass
63
- class Model:
64
- namespace: Namespace
65
- _object_types: OrderedDict[str, ObjectType]
66
- enumerations: OrderedDict[str, Enumeration]
67
- customizations: Customizations = field(default_factory=Customizations)
68
-
69
- @cached_property
70
- def object_types(self) -> OrderedDict[str, ObjectType]:
71
- result = OrderedDict()
72
- type_customizations = self.customizations.type_customizations
73
- for k, v in self._object_types.items():
74
- custom = type_customizations.get(k)
75
- if custom is None or not custom.drop:
76
- result[k] = v
77
- return result
78
-
17
+ class Model(core.Model):
79
18
  @cached_property
80
19
  def public_types(self) -> OrderedDict[str, Union[ObjectType, Enumeration]]:
81
20
  return OrderedDict(
@@ -91,10 +30,6 @@ class Model:
91
30
  if isinstance(t, InterfaceObjectType) and t.has_abstract_base
92
31
  ]
93
32
 
94
- def resolve_object_type(self, name: str) -> ObjectType:
95
- bare_name = name.split(".", maxsplit=1)[-1]
96
- return self.object_types[bare_name]
97
-
98
33
  def resolve_js_type(self, t: Type) -> str:
99
34
  js = js_type_from_gir(t.name)
100
35
  otype = self.object_types.get(js)
@@ -103,38 +38,7 @@ class Model:
103
38
  return js
104
39
 
105
40
 
106
- @dataclass
107
- class Namespace:
108
- name: str
109
- identifier_prefixes: str
110
- element: ET.Element
111
-
112
- @cached_property
113
- def type_elements(self) -> Mapping[str, ET.Element]:
114
- result = {}
115
- for toplevel in self.element.findall("./*[@name]", GIR_NAMESPACES):
116
- name = toplevel.get("name")
117
- result[name] = toplevel
118
- for callback in toplevel.findall("./callback", GIR_NAMESPACES):
119
- result[name + callback.get("name")] = callback
120
- return result
121
-
122
-
123
- @dataclass
124
- class ObjectType:
125
- name: str
126
- c_type: str
127
- get_type: str
128
- type_struct: str
129
- _parent: Optional[str]
130
- _constructors: List[ET.Element]
131
- _methods: List[ET.Element]
132
- _properties: List[ET.Element]
133
- _signals: List[ET.Element]
134
- resolve_type: ResolveTypeCallback
135
-
136
- model: Optional[Model]
137
-
41
+ class ObjectType(core.ObjectType):
138
42
  @cached_property
139
43
  def js_name(self) -> str:
140
44
  custom = self.customizations
@@ -150,24 +54,10 @@ class ObjectType:
150
54
  def abstract_base_c_type(self) -> str:
151
55
  return f"FdnAbstract{self.name}"
152
56
 
153
- @cached_property
154
- def parent(self) -> ObjectType:
155
- if self._parent is None:
156
- return None
157
- return self.model.resolve_object_type(self._parent)
158
-
159
57
  @property
160
58
  def is_public(self) -> bool:
161
59
  return not self.is_frida_list
162
60
 
163
- @cached_property
164
- def is_frida_options(self) -> bool:
165
- return self.c_type.startswith("Frida") and self.c_type.endswith("Options")
166
-
167
- @cached_property
168
- def is_frida_list(self) -> bool:
169
- return self.c_type.startswith("Frida") and self.c_type.endswith("List")
170
-
171
61
  @cached_property
172
62
  def needs_wrapper(self) -> bool:
173
63
  custom = self.customizations
@@ -180,10 +70,6 @@ class ObjectType:
180
70
  return True
181
71
  return self.wrapped_methods or self.wrapped_signals
182
72
 
183
- @cached_property
184
- def customizations(self) -> Optional[ObjectTypeCustomizations]:
185
- return self.model.customizations.type_customizations.get(self.name)
186
-
187
73
  @cached_property
188
74
  def c_symbol_prefix(self) -> str:
189
75
  return f"fdn_{to_snake_case(self.name)}"
@@ -200,190 +86,10 @@ class ObjectType:
200
86
  def abstract_base_c_cast_macro(self) -> str:
201
87
  return to_macro_case(self.abstract_base_c_type)
202
88
 
203
- @cached_property
204
- def constructors(self) -> List[Constructor]:
205
- constructors = []
206
- custom = self.customizations
207
- for element in self._constructors:
208
- if element.get("introspectable") == "0" or element.get("deprecated") == "1":
209
- continue
210
-
211
- name = element.get("name")
212
-
213
- if custom is not None:
214
- ccust = custom.constructor
215
- if ccust is not None and ccust.drop:
216
- continue
217
-
218
- (
219
- c_identifier,
220
- finish_c_identifier,
221
- param_list,
222
- has_closure_param,
223
- throws,
224
- result_element,
225
- ) = extract_callable_details(element, element, self, self.resolve_type)
226
- if has_closure_param or finish_c_identifier is not None:
227
- continue
228
-
229
- constructors.append(
230
- Constructor(
231
- name, c_identifier, finish_c_identifier, param_list, throws, self
232
- )
233
- )
234
- return constructors
235
-
236
- @cached_property
237
- def methods(self) -> List[Method]:
238
- methods = []
239
- c_prop_names = {prop.c_name for prop in self.properties}
240
- custom = self.customizations
241
- for element in self._methods:
242
- name = element.get("name")
243
-
244
- if (
245
- element.get("introspectable") == "0"
246
- or name.startswith("_")
247
- or name.endswith("_sync")
248
- or name.endswith("_finish")
249
- ):
250
- continue
251
-
252
- if custom is not None:
253
- mcust = custom.methods.get(name, None)
254
- if mcust is not None and mcust.drop:
255
- continue
256
-
257
- finish_func = element.get(f"{{{GLIB_NAMESPACE}}}finish-func")
258
- if finish_func is None:
259
- finish_func = f"{name}_finish"
260
- result_element = next(
261
- (m for m in self._methods if m.get("name") == finish_func), element
262
- )
263
-
264
- (
265
- c_identifier,
266
- finish_c_identifier,
267
- param_list,
268
- has_closure_param,
269
- throws,
270
- result_element,
271
- ) = extract_callable_details(
272
- element, result_element, self, self.resolve_type
273
- )
274
- if has_closure_param:
275
- continue
276
-
277
- retval_element = result_element.find(".//return-value", GIR_NAMESPACES)
278
- rettype = extract_type_from_entity(retval_element, self.resolve_type)
279
- if rettype is not None:
280
- if rettype.is_frida_options:
281
- continue
282
-
283
- nullable = retval_element.get("nullable") == "1"
284
-
285
- ownership_val = retval_element.get("transfer-ownership")
286
- transfer_ownership = (
287
- TransferOwnership[ownership_val]
288
- if ownership_val is not None
289
- else TransferOwnership.none
290
- )
291
-
292
- retval = ReturnValue(rettype, nullable, transfer_ownership, self)
293
- else:
294
- retval = None
295
-
296
- if element.get(f"{{{GLIB_NAMESPACE}}}get-property") is not None:
297
- is_property_accessor = True
298
- else:
299
- tokens = name.split("_", maxsplit=1)
300
- is_property_accessor = (
301
- len(tokens) == 2
302
- and tokens[0] in {"get", "set"}
303
- and tokens[1] in c_prop_names
304
- )
305
-
306
- methods.append(
307
- Method(
308
- name,
309
- c_identifier,
310
- finish_c_identifier,
311
- param_list,
312
- throws,
313
- retval,
314
- is_property_accessor,
315
- self,
316
- )
317
- )
318
- return methods
319
-
320
89
  @cached_property
321
90
  def wrapped_methods(self) -> List[Method]:
322
91
  return [m for m in self.methods if m.needs_wrapper]
323
92
 
324
- @cached_property
325
- def properties(self) -> List[Property]:
326
- properties = []
327
- custom = self.customizations
328
- for element in self._properties:
329
- name = element.get("name")
330
-
331
- if custom is not None:
332
- pcust = custom.properties.get(name, None)
333
- if pcust is not None and pcust.drop:
334
- continue
335
-
336
- c_name = name.replace("-", "_")
337
- type = extract_type_from_entity(element, self.resolve_type)
338
- if type.is_frida_options:
339
- continue
340
- writable = element.get("writable") == "1"
341
- construct_only = element.get("construct-only") == "1"
342
-
343
- getter = element.get("getter")
344
- if getter is None:
345
- getter = f"get_{c_name}"
346
-
347
- setter = element.get("setter")
348
- if setter is None and writable and not construct_only:
349
- setter = f"set_{c_name}"
350
-
351
- properties.append(
352
- Property(
353
- name,
354
- c_name,
355
- type,
356
- writable,
357
- construct_only,
358
- getter,
359
- setter,
360
- self,
361
- )
362
- )
363
- return properties
364
-
365
- @cached_property
366
- def signals(self) -> List[Signal]:
367
- signals = []
368
- custom = self.customizations
369
- for element in self._signals:
370
- name = element.get("name")
371
-
372
- if custom is not None:
373
- scust = custom.signals.get(name, None)
374
- if scust is not None and scust.drop:
375
- continue
376
-
377
- c_name = name.replace("-", "_")
378
- param_list = extract_parameters(
379
- element.findall("./parameters/parameter", GIR_NAMESPACES),
380
- nullable_implies_optional=False,
381
- object_type=self,
382
- resolve_type=self.resolve_type,
383
- )
384
- signals.append(Signal(name, c_name, param_list, self))
385
- return signals
386
-
387
93
  @cached_property
388
94
  def wrapped_signals(self) -> List[Signal]:
389
95
  return [s for s in self.signals if s.needs_wrapper]
@@ -391,14 +97,13 @@ class ObjectType:
391
97
 
392
98
  @dataclass
393
99
  class ClassObjectType(ObjectType):
394
- _implements: List[str]
100
+ _implements: List[str] = field(default_factory=list)
395
101
 
396
102
  @cached_property
397
103
  def implements(self) -> List[InterfaceObjectType]:
398
104
  return [self.model.resolve_object_type(i) for i in self._implements]
399
105
 
400
106
 
401
- @dataclass
402
107
  class InterfaceObjectType(ObjectType):
403
108
  @cached_property
404
109
  def has_abstract_base(self) -> bool:
@@ -408,27 +113,7 @@ class InterfaceObjectType(ObjectType):
408
113
  return not custom.drop_abstract_base
409
114
 
410
115
 
411
- @dataclass
412
- class Procedure:
413
- name: str
414
- c_identifier: str
415
- finish_c_identifier: Optional[str]
416
- parameters: List[Parameter]
417
- throws: bool
418
-
419
- @property
420
- def is_async(self) -> bool:
421
- return self.finish_c_identifier is not None
422
-
423
- @cached_property
424
- def input_parameters(self) -> List[Parameter]:
425
- return [p for p in self.parameters if p.direction != Direction.OUT]
426
-
427
-
428
- @dataclass
429
- class Constructor(Procedure):
430
- object_type: ObjectType
431
-
116
+ class Constructor(core.Constructor):
432
117
  @cached_property
433
118
  def param_typings(self) -> List[str]:
434
119
  custom = self.customizations
@@ -451,13 +136,7 @@ class Constructor(Procedure):
451
136
  return custom.constructor
452
137
 
453
138
 
454
- @dataclass
455
- class Method(Procedure):
456
- return_value: Optional[ReturnValue]
457
- is_property_accessor: bool
458
-
459
- object_type: ObjectType
460
-
139
+ class Method(core.Method):
461
140
  @cached_property
462
141
  def js_name(self) -> str:
463
142
  custom = self.customizations
@@ -568,18 +247,7 @@ class Method(Procedure):
568
247
  return self.parameters[0].type
569
248
 
570
249
 
571
- @dataclass
572
- class Property:
573
- name: str
574
- c_name: str
575
- type: Type
576
- writable: bool
577
- construct_only: bool
578
- getter: Optional[str]
579
- setter: Optional[str]
580
-
581
- object_type: ObjectType
582
-
250
+ class Property(core.Property):
583
251
  @cached_property
584
252
  def js_name(self) -> str:
585
253
  custom = self.customizations
@@ -604,14 +272,7 @@ class Property:
604
272
  return custom.properties.get(self.name)
605
273
 
606
274
 
607
- @dataclass
608
- class Signal:
609
- name: str
610
- c_name: str
611
- parameters: List[Parameter]
612
-
613
- object_type: ObjectType
614
-
275
+ class Signal(core.Signal):
615
276
  @cached_property
616
277
  def js_name(self) -> str:
617
278
  return to_camel_case(self.c_name)
@@ -664,20 +325,7 @@ class Signal:
664
325
  return custom.signals.get(self.name)
665
326
 
666
327
 
667
- TransferOwnership = Enum("TransferOwnership", ["none", "full", "container"])
668
-
669
-
670
- @dataclass
671
- class Parameter:
672
- name: str
673
- type: Type
674
- optional: bool
675
- nullable: bool
676
- transfer_ownership: TransferOwnership
677
- direction: Direction
678
-
679
- object_type: ObjectType
680
-
328
+ class Parameter(core.Parameter):
681
329
  @cached_property
682
330
  def js_name(self) -> str:
683
331
  return to_camel_case(self.name)
@@ -703,14 +351,7 @@ class Parameter:
703
351
  return self.type.destroy_func
704
352
 
705
353
 
706
- @dataclass
707
- class ReturnValue:
708
- type: Type
709
- nullable: bool
710
- transfer_ownership: TransferOwnership
711
-
712
- object_type: ObjectType
713
-
354
+ class ReturnValue(core.ReturnValue):
714
355
  @cached_property
715
356
  def ctyping(self) -> str:
716
357
  return self.type.c
@@ -729,55 +370,7 @@ class ReturnValue:
729
370
  return self.type.destroy_func
730
371
 
731
372
 
732
- @dataclass
733
- class Type:
734
- name: str
735
- nick: str
736
- c: str
737
- default_value: Optional[str]
738
- copy_func: Optional[str]
739
- destroy_func: Optional[str]
740
-
741
- @cached_property
742
- def from_pointer_func(self) -> Optional[str]:
743
- if self.name in {"gssize", "gsize", "glong", "gulong", "gint64", "guint64"}:
744
- return "GPOINTER_TO_SIZE"
745
- if self.name in {"gint", "gint8", "gint16", "gint32"}:
746
- return "GPOINTER_TO_INT"
747
- if self.name in {"gboolean", "guint", "guint8", "guint16", "guint32"}:
748
- return "GPOINTER_TO_UINT"
749
- return None
750
-
751
- @cached_property
752
- def to_pointer_func(self) -> Optional[str]:
753
- if self.name in {"gssize", "gsize", "glong", "gulong", "gint64", "guint64"}:
754
- return "GSIZE_TO_POINTER"
755
- if self.name in {"gint", "gint8", "gint16", "gint32"}:
756
- return "GINT_TO_POINTER"
757
- if self.name in {"gboolean", "guint", "guint8", "guint16", "guint32"}:
758
- return "GUINT_TO_POINTER"
759
- return None
760
-
761
- @cached_property
762
- def is_frida_options(self) -> bool:
763
- return self.c.startswith("Frida") and self.c.endswith("Options *")
764
-
765
-
766
- class Direction(Enum):
767
- IN = "in"
768
- OUT = "out"
769
- INOUT = "inout"
770
-
771
-
772
- @dataclass
773
- class Enumeration:
774
- name: str
775
- c_type: str
776
- get_type: str
777
- _members: List[ET.Element]
778
-
779
- model: Optional[Model]
780
-
373
+ class Enumeration(core.Enumeration):
781
374
  @property
782
375
  def js_name(self) -> str:
783
376
  return self.name
@@ -786,32 +379,16 @@ class Enumeration:
786
379
  def prefixed_js_name(self) -> str:
787
380
  return self.name
788
381
 
789
- @cached_property
790
- def members(self) -> List[EnumerationMember]:
791
- members = []
792
- for element in self._members:
793
- members.append(EnumerationMember(element.get("name"), self))
794
- return members
795
-
796
382
  @property
797
383
  def is_frida_options(self) -> bool:
798
384
  return False
799
385
 
800
- @cached_property
801
- def customizations(self) -> Optional[EnumerationCustomizations]:
802
- return self.model.customizations.type_customizations.get(self.name)
803
-
804
386
  @cached_property
805
387
  def c_symbol_prefix(self) -> str:
806
388
  return f"fdn_{to_snake_case(self.name)}"
807
389
 
808
390
 
809
- @dataclass
810
- class EnumerationMember:
811
- name: str
812
-
813
- enumeration: Enumeration
814
-
391
+ class EnumerationMember(core.EnumerationMember):
815
392
  @cached_property
816
393
  def js_name(self) -> str:
817
394
  custom = self.customizations
@@ -831,6 +408,18 @@ class EnumerationMember:
831
408
  return custom.members.get(self.name)
832
409
 
833
410
 
411
+ @dataclass
412
+ class Customizations:
413
+ custom_types: Mapping[str, CustomType] = field(default_factory=OrderedDict)
414
+ type_customizations: Mapping[str, TypeCustomizations] = field(
415
+ default_factory=OrderedDict
416
+ )
417
+ facade_exports: List[str] = field(default_factory=list)
418
+ facade_code: str = ""
419
+ helper_imports: List[str] = field(default_factory=list)
420
+ helper_code: str = ""
421
+
422
+
834
423
  @dataclass
835
424
  class CustomType:
836
425
  kind: CustomTypeKind
@@ -940,359 +529,79 @@ class EnumerationMemberCustomizations:
940
529
  js_name: Optional[str] = None
941
530
 
942
531
 
943
- def parse_gir(file_path: str, dependencies: Sequence[Model]) -> Model:
944
- tree = ET.parse(file_path)
945
-
946
- el = tree.getroot().find("./namespace", GIR_NAMESPACES)
947
- namespace = Namespace(
948
- el.get("name"), el.get(f"{{{C_NAMESPACE}}}identifier-prefixes"), el
532
+ def _make_class(
533
+ *,
534
+ name,
535
+ c_type,
536
+ get_type,
537
+ type_struct,
538
+ parent,
539
+ constructors,
540
+ methods,
541
+ properties,
542
+ signals,
543
+ implements,
544
+ resolve_type,
545
+ model,
546
+ ):
547
+ return ClassObjectType(
548
+ name,
549
+ c_type,
550
+ get_type,
551
+ type_struct,
552
+ parent,
553
+ constructors,
554
+ methods,
555
+ properties,
556
+ signals,
557
+ resolve_type,
558
+ model,
559
+ implements,
949
560
  )
950
561
 
951
- def resolve_type(name: str) -> Tuple[str, ET.Element]:
952
- assert (
953
- name not in PRIMITIVE_GIR_TYPES
954
- ), f"unexpectedly asked to resolve primitive type: {name}"
955
-
956
- tokens = name.split(".", maxsplit=1)
957
- if len(tokens) == 2:
958
- ns_name, bare_name = tokens
959
- if ns_name == namespace.name:
960
- ns = namespace
961
- else:
962
- ns = next(
963
- (
964
- dep.namespace
965
- for dep in dependencies
966
- if dep.namespace.name == ns_name
967
- ),
968
- None,
969
- )
970
- if ns is None:
971
- assert ns is not None, f"unable to resolve namespace {ns_name}"
972
- else:
973
- ns = namespace
974
- bare_name = name
975
- qualified_name = f"{ns.name}.{bare_name}"
976
-
977
- element = ns.type_elements.get(bare_name)
978
- assert element is not None, f"unable to resolve type {bare_name}"
979
-
980
- return (qualified_name, element)
981
562
 
982
- object_types = OrderedDict()
983
-
984
- for element in namespace.element.findall("./class", GIR_NAMESPACES):
985
- name = element.get("name")
986
- c_type = element.get(f"{{{C_NAMESPACE}}}type")
987
- get_type = element.get(f"{{{GLIB_NAMESPACE}}}get-type")
988
- type_struct = element.get(f"{{{GLIB_NAMESPACE}}}type-struct")
989
- if type_struct is not None:
990
- type_struct = namespace.identifier_prefixes + type_struct
991
- else:
992
- type_struct = c_type + "Class"
993
- parent = element.get("parent")
994
- if parent is not None:
995
- parent, _ = resolve_type(parent)
996
- constructors = element.findall(".//constructor", GIR_NAMESPACES)
997
- methods = element.findall(".//method", GIR_NAMESPACES)
998
- properties = element.findall(".//property", GIR_NAMESPACES)
999
- signals = element.findall(".//glib:signal", GIR_NAMESPACES)
1000
- implements = [
1001
- e.get("name") for e in element.findall(".//implements", GIR_NAMESPACES)
1002
- ]
1003
-
1004
- object_types[name] = ClassObjectType(
1005
- name,
1006
- c_type,
1007
- get_type,
1008
- type_struct,
1009
- parent,
1010
- constructors,
1011
- methods,
1012
- properties,
1013
- signals,
1014
- resolve_type,
1015
- None,
1016
- implements,
1017
- )
1018
-
1019
- for element in namespace.element.findall("./interface", GIR_NAMESPACES):
1020
- name = element.get("name")
1021
- c_type = element.get(f"{{{C_NAMESPACE}}}type")
1022
- get_type = element.get(f"{{{GLIB_NAMESPACE}}}get-type")
1023
- type_struct = element.get(f"{{{GLIB_NAMESPACE}}}type-struct")
1024
- if type_struct is not None:
1025
- type_struct = namespace.identifier_prefixes + type_struct
1026
- else:
1027
- type_struct = c_type + "Iface"
1028
- prereq = element.find(".//prerequisite", GIR_NAMESPACES)
1029
- parent = prereq.get("name") if prereq is not None else None
1030
- if parent is not None:
1031
- parent, _ = resolve_type(parent)
1032
- constructors = []
1033
- methods = element.findall(".//method", GIR_NAMESPACES)
1034
- properties = element.findall(".//property", GIR_NAMESPACES)
1035
- signals = element.findall(".//glib:signal", GIR_NAMESPACES)
1036
-
1037
- object_types[name] = InterfaceObjectType(
1038
- name,
1039
- c_type,
1040
- get_type,
1041
- type_struct,
1042
- parent,
1043
- constructors,
1044
- methods,
1045
- properties,
1046
- signals,
1047
- resolve_type,
1048
- None,
1049
- )
1050
-
1051
- enumerations = OrderedDict()
1052
-
1053
- for element in namespace.element.findall("./enumeration", GIR_NAMESPACES):
1054
- if element.get(f"{{{GLIB_NAMESPACE}}}error-domain") is not None:
1055
- continue
1056
- enum_name = element.get("name")
1057
- enum_c_type = element.get(f"{{{C_NAMESPACE}}}type")
1058
- get_type = element.get(f"{{{GLIB_NAMESPACE}}}get-type")
1059
- members = element.findall(".//member", GIR_NAMESPACES)
1060
- enumerations[enum_name] = Enumeration(
1061
- enum_name, enum_c_type, get_type, members, None
1062
- )
1063
-
1064
- model = Model(namespace, object_types, enumerations)
1065
-
1066
- for t in object_types.values():
1067
- t.model = model
1068
- for t in enumerations.values():
1069
- t.model = model
1070
-
1071
- return model
1072
-
1073
-
1074
- def extract_callable_details(
1075
- element: ET.Element,
1076
- result_element: ET.Element,
1077
- object_type: ObjectType,
1078
- resolve_type: ResolveTypeCallback,
1079
- ) -> Tuple[str, Optional[str], List[Parameter], bool, bool, ET.Element]:
1080
- c_identifier = element.get(f"{{{C_NAMESPACE}}}identifier")
1081
-
1082
- parameters = element.findall("./parameters/parameter", GIR_NAMESPACES)
1083
- full_param_list = extract_parameters(
1084
- parameters,
1085
- nullable_implies_optional=True,
1086
- object_type=object_type,
1087
- resolve_type=resolve_type,
1088
- )
1089
- param_list = list(all_regular_parameters(full_param_list))
1090
- has_closure_param = any((param.get("closure") == "1" for param in parameters))
1091
-
1092
- is_async = any(
1093
- param.type.name == "Gio.AsyncReadyCallback" for param in full_param_list
1094
- )
1095
- if not is_async:
1096
- result_element = element
1097
-
1098
- finish_c_identifier = (
1099
- result_element.get(f"{{{C_NAMESPACE}}}identifier") if is_async else None
563
+ def _make_interface(
564
+ *,
565
+ name,
566
+ c_type,
567
+ get_type,
568
+ type_struct,
569
+ parent,
570
+ constructors,
571
+ methods,
572
+ properties,
573
+ signals,
574
+ resolve_type,
575
+ model,
576
+ ):
577
+ return InterfaceObjectType(
578
+ name,
579
+ c_type,
580
+ get_type,
581
+ type_struct,
582
+ parent,
583
+ constructors,
584
+ methods,
585
+ properties,
586
+ signals,
587
+ resolve_type,
588
+ model,
1100
589
  )
1101
590
 
1102
- throws = result_element.get("throws") == "1"
1103
-
1104
- return (
1105
- c_identifier,
1106
- finish_c_identifier,
1107
- param_list,
1108
- has_closure_param,
1109
- throws,
1110
- result_element,
1111
- )
1112
-
1113
-
1114
- def extract_parameters(
1115
- parameter_elements: List[ET.Element],
1116
- nullable_implies_optional: bool,
1117
- object_type: ObjectType,
1118
- resolve_type: ResolveTypeCallback,
1119
- ) -> List[Parameter]:
1120
- entries = []
1121
- for param in parameter_elements:
1122
- nullable = param.get("nullable") == "1"
1123
- entries.append((param, nullable))
1124
-
1125
- last_required_index = None
1126
- for i, (param, nullable) in enumerate(entries):
1127
- optional = nullable and nullable_implies_optional
1128
- if not optional:
1129
- last_required_index = i
1130
-
1131
- param_list = []
1132
- for i, (param, nullable) in enumerate(entries):
1133
- name = param.get("name")
1134
- type = extract_type_from_entity(param, resolve_type)
1135
-
1136
- if last_required_index is None or i > last_required_index:
1137
- optional = nullable and nullable_implies_optional
1138
- else:
1139
- optional = False
1140
-
1141
- ownership_val = param.get("transfer-ownership")
1142
- transfer_ownership = (
1143
- TransferOwnership[ownership_val]
1144
- if ownership_val is not None
1145
- else TransferOwnership.none
1146
- )
1147
-
1148
- raw_direction = param.get("direction")
1149
- direction = (
1150
- Direction(raw_direction) if raw_direction is not None else Direction.IN
1151
- )
1152
-
1153
- param_list.append(
1154
- Parameter(
1155
- name,
1156
- type,
1157
- optional,
1158
- nullable,
1159
- transfer_ownership,
1160
- direction,
1161
- object_type,
1162
- )
1163
- )
1164
- return param_list
1165
-
1166
-
1167
- def all_regular_parameters(parameters: List[Parameter]) -> Iterator[Parameter]:
1168
- callback_index = None
1169
- for i, param in enumerate(parameters):
1170
- if param.type.name == "Gio.AsyncReadyCallback":
1171
- callback_index = i
1172
- continue
1173
-
1174
- if callback_index is not None and i == callback_index + 1:
1175
- continue
1176
-
1177
- yield param
1178
-
1179
-
1180
- def extract_type_from_entity(
1181
- parent_element: ET.Element, resolve_type: ResolveTypeCallback
1182
- ) -> Optional[Type]:
1183
- child = parent_element.find("type", GIR_NAMESPACES)
1184
- if child is None:
1185
- child = parent_element.find("array", GIR_NAMESPACES)
1186
- assert child is not None
1187
- element_type = extract_type_from_entity(child, resolve_type)
1188
- if element_type.name == "utf8":
1189
- return Type(
1190
- "utf8[]",
1191
- "strv",
1192
- "gchar **",
1193
- "NULL",
1194
- "g_strdupv",
1195
- "g_strfreev",
1196
- )
1197
- elif element_type.name == "gchar":
1198
- return Type("char[]", "chararray", "gchar *", "NULL", "NULL", "NULL")
1199
- elif element_type.name == "GObject.Value":
1200
- return Type("Value[]", "valuearray", "GValue *", "NULL", "NULL", "NULL")
1201
- else:
1202
- assert (
1203
- element_type.name == "guint8"
1204
- ), f"unsupported array type: {element_type.name}"
1205
- return Type("uint8[]", "bytearray", "guint8 *", "NULL", "NULL", "NULL")
1206
-
1207
- return parse_type(child, resolve_type)
1208
-
1209
-
1210
- def parse_type(
1211
- element: ET.Element, resolve_type: ResolveTypeCallback
1212
- ) -> Optional[Type]:
1213
- name = element.get("name")
1214
- assert name is not None
1215
- if name == "none":
1216
- return None
1217
-
1218
- is_primitive = name in PRIMITIVE_GIR_TYPES
1219
- c_type = element.get(f"{{{C_NAMESPACE}}}type")
1220
-
1221
- core_tag = None
1222
- if is_primitive:
1223
- type_element = element
1224
- if c_type is None:
1225
- c_type = name
1226
- else:
1227
- name, type_element = resolve_type(name)
1228
- if type_element.tag.startswith(CORE_TAG_PREFIX):
1229
- core_tag = type_element.tag[len(CORE_TAG_PREFIX) :]
1230
- c_type = type_element.get(f"{{{C_NAMESPACE}}}type")
1231
- if core_tag in {"class", "interface", "record"}:
1232
- c_type += "*"
1233
-
1234
- nick = type_nick_from_name(name, element, resolve_type)
1235
- c = c_type.replace("*", " *")
1236
-
1237
- default_value = "NULL" if "*" in c else None
1238
-
1239
- if name == "utf8":
1240
- copy_func = "g_strdup"
1241
- destroy_func = "g_free"
1242
- elif name == "utf8[]":
1243
- copy_func = "g_strdupv"
1244
- destroy_func = "g_strfreev"
1245
- elif name == "GLib.HashTable":
1246
- copy_func = "g_hash_table_ref"
1247
- destroy_func = "g_hash_table_unref"
1248
- elif name == "GLib.Quark":
1249
- copy_func = None
1250
- destroy_func = None
1251
- elif name == "GObject.Value":
1252
- copy_func = "g_value_copy"
1253
- destroy_func = "g_value_reset"
1254
- elif name == "GObject.Closure":
1255
- copy_func = "g_closure_ref"
1256
- destroy_func = "g_closure_unref"
1257
- elif core_tag in {"class", "interface"}:
1258
- copy_func = "g_object_ref"
1259
- destroy_func = "g_object_unref"
1260
- elif is_primitive or core_tag in {"bitfield", "callback", "enumeration"}:
1261
- copy_func = None
1262
- destroy_func = None
1263
- else:
1264
- copy_func = type_element.get("copy-function")
1265
- destroy_func = type_element.get("free-function")
1266
- assert (
1267
- destroy_func is not None
1268
- ), f"unable to resolve destroy function for {name}, core_tag={core_tag}"
1269
-
1270
- return Type(name, nick, c, default_value, copy_func, destroy_func)
1271
-
1272
-
1273
- def type_nick_from_name(
1274
- name: str, element: ET.Element, resolve_type: ResolveTypeCallback
1275
- ) -> str:
1276
- if name == "GLib.PollFD":
1277
- return "pollfd"
1278
-
1279
- tokens = name.split(".", maxsplit=1)
1280
- if len(tokens) == 1:
1281
- result = tokens[0]
1282
- if result.startswith("g"):
1283
- result = result[1:]
1284
- else:
1285
- result = to_snake_case(tokens[1])
1286
-
1287
- if result == "hash_table":
1288
- key_type = parse_type(element[0], resolve_type)
1289
- value_type = parse_type(element[1], resolve_type)
1290
- assert (
1291
- key_type.name == "utf8" and value_type.name == "GLib.Variant"
1292
- ), "only GHashTable<string, Variant> is supported for now"
1293
- result = "vardict"
1294
591
 
1295
- return result
592
+ FACTORY = core.Factory(
593
+ class_object_type=_make_class,
594
+ interface_object_type=_make_interface,
595
+ constructor=Constructor,
596
+ method=Method,
597
+ parameter=Parameter,
598
+ return_value=ReturnValue,
599
+ signal=Signal,
600
+ property_=Property,
601
+ enumeration=Enumeration,
602
+ enumeration_member=EnumerationMember,
603
+ model=Model,
604
+ )
1296
605
 
1297
606
 
1298
607
  def js_type_from_gir(name: str) -> str:
@@ -1315,45 +624,3 @@ def js_type_from_gir(name: str) -> str:
1315
624
  if name.startswith("Frida.") and name.endswith("List"):
1316
625
  return name[6:-4] + "[]"
1317
626
  return name.split(".")[-1]
1318
-
1319
-
1320
- def to_snake_case(name: str) -> str:
1321
- result = []
1322
- i = 0
1323
- n = len(name)
1324
- while i < n:
1325
- if name[i].isupper():
1326
- if i > 0:
1327
- result.append("_")
1328
- start = i
1329
- if i + 1 < n and name[i + 1].islower():
1330
- while i + 1 < n and name[i + 1].islower():
1331
- i += 1
1332
- else:
1333
- while i + 1 < n and name[i + 1].isupper():
1334
- i += 1
1335
- if i + 1 < n:
1336
- i -= 1
1337
- result.append(name[start : i + 1].lower())
1338
- else:
1339
- result.append(name[i])
1340
- i += 1
1341
- return "".join(result)
1342
-
1343
-
1344
- def to_pascal_case(name: str) -> str:
1345
- return "".join(word.capitalize() for word in name.split("_"))
1346
-
1347
-
1348
- def to_camel_case(name: str) -> str:
1349
- words = name.split("_")
1350
- return words[0] + "".join(word.capitalize() for word in words[1:])
1351
-
1352
-
1353
- def to_macro_case(identifier: str) -> str:
1354
- result = []
1355
- for i, char in enumerate(identifier):
1356
- if char.isupper() and i != 0:
1357
- result.append("_")
1358
- result.append(char)
1359
- return "".join(result).upper()