1# SPDX-FileCopyrightText: 2020 GNOME Foundation
2# SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later
3
4import typing as T
5
6from .. import log
7
8
9class Doc:
10    """A documentation node, pointing to the source code"""
11    def __init__(self, content: str, filename: str, line: int, version: str = None, stability: str = None):
12        self.content = content
13        self.filename = filename
14        self.line = line
15        self.version = version
16        self.stability = stability
17
18    def __str__(self):
19        return self.content
20
21
22class SourcePosition:
23    """A location inside the source code"""
24    def __init__(self, filename: str, line: int):
25        self.filename = filename
26        self.line = line
27
28    def __str__(self):
29        return f'{self.filename}:{self.line}'
30
31
32class Attribute:
33    """A user-defined annotation"""
34    def __init__(self, name: str, value: T.Optional[str]):
35        self.name = name
36        self.value = value
37
38
39class CInclude:
40    """A C include header"""
41    def __init__(self, name: str):
42        self.name = name
43
44
45class Include:
46    """A GIR include"""
47    def __init__(self, name: str, version: str = None):
48        self.name = name
49        self.version = version
50
51    def __str__(self):
52        if self.version is not None:
53            return f"{self.name}-{self.version}"
54        return f"{self.name}"
55
56    def girfile(self) -> str:
57        if self.version is not None:
58            return f"{self.name}-{self.version}.gir"
59        return f"{self.name}.gir"
60
61
62class Package:
63    """Pkg-config containing the library"""
64    def __init__(self, name: str):
65        self.name = name
66
67
68class Info:
69    """Base information for most types"""
70    def __init__(self, introspectable: bool = True, deprecated: T.Optional[str] = None,
71                 deprecated_version: T.Optional[str] = None, version: str = None,
72                 stability: str = None):
73        self.introspectable = introspectable
74        self.deprecated_msg = deprecated
75        self.deprecated_version = deprecated_version
76        self.version = version
77        self.stability = stability
78        self.attributes: T.Mapping[str, T.Optional[str]] = {}
79        self.doc: T.Optional[Doc] = None
80        self.source_position: T.Optional[SourcePosition] = None
81
82    def add_attribute(self, name: str, value: T.Optional[str] = None) -> None:
83        self.attributes[name] = value
84
85
86class GIRElement:
87    """Base type for elements inside the GIR"""
88    def __init__(self, name: T.Optional[str] = None, namespace: T.Optional[str] = None):
89        self.name = name
90        self.namespace = namespace
91        if self.namespace is None:
92            if self.name is not None and '.' in self.name:
93                self.namespace = self.name.split('.')[0]
94        self.info = Info()
95
96    def set_introspectable(self, introspectable: bool) -> None:
97        """Set whether the symbol is introspectable"""
98        self.info.introspectable = introspectable
99
100    @property
101    def introspectable(self):
102        return self.info.introspectable
103
104    def set_version(self, version: str) -> None:
105        """Set the version of the symbol"""
106        self.info.version = version
107
108    def set_stability(self, stability: str) -> None:
109        """Set the stability of the symbol"""
110        self.info.stability = stability
111
112    @property
113    def stability(self):
114        return self.info.stability
115
116    def set_doc(self, doc: Doc) -> None:
117        """Set the documentation for the element"""
118        self.info.doc = doc
119
120    @property
121    def doc(self):
122        return self.info.doc
123
124    def set_source_position(self, pos: SourcePosition) -> None:
125        """Set the position in the source code for the element"""
126        self.info.source_position = pos
127
128    @property
129    def source_position(self) -> T.Optional[T.Tuple[str, int]]:
130        if self.info.source_position is None:
131            return None
132        return self.info.source_position.filename, self.info.source_position.line
133
134    def set_deprecated(self, doc: T.Optional[str] = None, since_version: T.Optional[str] = None) -> None:
135        """Set the deprecation annotations for the element"""
136        self.info.deprecated_msg = doc
137        self.info.deprecated_version = since_version
138
139    def set_attributes(self, attrs: T.Mapping[str, T.Optional[str]]) -> None:
140        """Add an annotation to the symbol"""
141        for name, value in attrs.items():
142            self.info.add_attribute(name, value)
143
144    @property
145    def attributes(self) -> T.Mapping[str, T.Optional[str]]:
146        return self.info.attributes
147
148    @property
149    def available_since(self) -> T.Optional[str]:
150        return self.info.version
151
152    @property
153    def deprecated_since(self) -> T.Optional[T.Tuple[str, str]]:
154        if not self.info.deprecated_msg:
155            return None
156        version = self.info.deprecated_version
157        message = self.info.deprecated_msg
158        if message is None:
159            message = "Please do not use it in newly written code"
160        return (version, message)
161
162
163class Type(GIRElement):
164    """Base class for all Type nodes"""
165    def __init__(self, name: str, ctype: T.Optional[str] = None, namespace: T.Optional[str] = None, is_fundamental: bool = False):
166        super().__init__(name=name, namespace=namespace)
167        self.ctype = ctype
168        self.is_fundamental = is_fundamental
169
170    def __eq__(self, other):
171        if isinstance(other, Type):
172            if self.namespace is not None:
173                return self.namespace == other.namespace and self.name == other.name
174            elif self.ctype is not None:
175                return self.name == other.name and self.ctype == other.ctype
176            else:
177                return self.name == other.name
178        elif isinstance(other, str):
179            return self.name == other
180        else:
181            return False
182
183    def __cmp__(self, other):
184        if self.ctype is not None:
185            return self.name == other.name and self.ctype == other.ctype
186        return self.name == other.name
187
188    def __repr__(self):
189        return f"Type({self.fqtn}, {self.ctype})"
190
191    @property
192    def resolved(self):
193        return self.ctype is not None
194
195    @property
196    def base_ctype(self):
197        if self.ctype is None:
198            return None
199        return self.ctype.replace('*', '')
200
201    @property
202    def fqtn(self):
203        if self.is_fundamental:
204            return self.name
205        elif '.' in self.name:
206            return self.name
207        elif self.namespace is not None:
208            return f"{self.namespace}.{self.name}"
209        else:
210            return None
211
212
213class ArrayType(GIRElement):
214    """Base class for Array nodes"""
215    def __init__(self, name: str, value_type: Type, ctype: str = None, zero_terminated: bool = False,
216                 fixed_size: int = -1, length: int = -1):
217        super().__init__(name)
218        self.ctype = ctype
219        self.zero_terminated = zero_terminated
220        self.fixed_size = fixed_size
221        self.length = length
222        self.value_type = value_type
223        self.is_fundamental = False
224
225
226class ListType(GIRElement):
227    """Type class for List nodes"""
228    def __init__(self, name: str, value_type: Type, ctype: str = None):
229        super().__init__(name)
230        self.ctype = ctype
231        self.value_type = value_type
232        self.is_fundamental = False
233
234
235class MapType(GIRElement):
236    """Type class for Map nodes"""
237    def __init__(self, name: str, key_type: Type, value_type: Type, ctype: str = None):
238        super().__init__(name)
239        self.ctype = ctype
240        self.key_type = key_type
241        self.value_type = value_type
242        self.is_fundamental = False
243
244
245class GType:
246    """Base class for GType information"""
247    def __init__(self, type_name: str, get_type: str, type_struct: T.Optional[str] = None):
248        self.type_name = type_name
249        self.get_type = get_type
250        self.type_struct = type_struct
251
252
253class VoidType(Type):
254    def __init__(self):
255        super().__init__(name='none', ctype='void')
256
257    def __str__(self):
258        return "void"
259
260
261class VarArgs(Type):
262    def __init__(self):
263        super().__init__(name='none', ctype='')
264
265    def __str__(self):
266        return "..."
267
268
269class Alias(Type):
270    """Alias to a Type"""
271    def __init__(self, name: str, namespace: str, ctype: str, target: Type):
272        super().__init__(name=name, ctype=ctype, namespace=namespace)
273        self.target = target
274
275
276class Constant(Type):
277    """A constant"""
278    def __init__(self, name: str, namespace: str, ctype: str, target: Type, value: str):
279        super().__init__(name=name, ctype=ctype, namespace=namespace)
280        self.target = target
281        self.value = value
282
283
284class Parameter(GIRElement):
285    """A callable parameter"""
286    def __init__(self, name: str, direction: str, transfer: str, target: Type = None, caller_allocates: bool = False,
287                 optional: bool = False, nullable: bool = False, closure: int = -1, destroy: int = -1,
288                 scope: str = None):
289        super().__init__(name)
290        self.direction = direction
291        self.transfer = transfer
292        self.caller_allocates = caller_allocates
293        self.optional = optional
294        self.nullable = nullable
295        self.scope = scope
296        self.closure = closure
297        self.destroy = destroy
298        if target is None:
299            self.target: Type = VoidType()
300        else:
301            self.target = target
302
303
304class ReturnValue(GIRElement):
305    """A callable's return value"""
306    def __init__(self, transfer: str, target: Type, nullable: bool = False, closure: int = -1, destroy: int = -1, scope: str = None):
307        super().__init__()
308        self.transfer = transfer
309        self.nullable = nullable
310        self.scope = scope
311        self.closure = closure
312        self.destroy = destroy
313        if target is None:
314            self.target: Type = VoidType()
315        else:
316            self.target = target
317
318
319class Callable(GIRElement):
320    """A callable symbol: function, method, function-macro, ..."""
321    def __init__(self, name: str, namespace: T.Optional[str], identifier: T.Optional[str], throws: bool = False):
322        super().__init__(name=name, namespace=namespace)
323        self.identifier = identifier
324        self.parameters: T.List[Parameter] = []
325        self.return_value: T.Optional[ReturnValue] = None
326        self.throws: bool = throws
327        self.moved_to: T.Optional[str] = None
328        self.shadows: T.Optional[str] = None
329        self.shadowed_by: T.Optional[str] = None
330
331    def add_parameter(self, param: Parameter) -> None:
332        self.parameters.append(param)
333
334    def set_parameters(self, params: T.List[Parameter]) -> None:
335        self.parameters.extend(params)
336
337    def set_return_value(self, res: ReturnValue) -> None:
338        self.return_value = res
339
340    def set_shadows(self, func: str) -> None:
341        self.shadows = func
342
343    def set_shadowed_by(self, func: str) -> None:
344        self.shadowed_by = func
345
346    def set_moved_to(self, func: str) -> None:
347        self.moved_to = func
348
349    def __contains__(self, param):
350        if isinstance(param, str):
351            for p in self.parameters:
352                if p.name == param:
353                    return True
354        elif isinstance(param, Parameter):
355            return param in self.parameters
356        elif isinstance(param, ReturnValue):
357            return param == self.return_value
358        return False
359
360
361class FunctionMacro(Callable):
362    def __init__(self, name: str, namespace: T.Optional[str], identifier: str):
363        super().__init__(name, namespace, identifier)
364
365
366class Function(Callable):
367    def __init__(self, name: str, namespace: T.Optional[str], identifier: str, throws: bool = False):
368        super().__init__(name, namespace, identifier, throws)
369
370
371class Method(Callable):
372    def __init__(self, name: str, identifier: str, instance_param: Parameter, throws: bool = False,
373                 set_property: T.Optional[str] = None, get_property: T.Optional[str] = None):
374        super().__init__(name, None, identifier, throws)
375        self.instance_param = instance_param
376        self.set_property = set_property
377        self.get_property = get_property
378
379    def __contains__(self, param):
380        if isinstance(param, Parameter) and param == self.instance_param:
381            return True
382        return super().__contains__(self, param)
383
384
385class VirtualMethod(Callable):
386    def __init__(self, name: str, identifier: str, invoker: str, instance_param: Parameter, throws: bool = False):
387        super().__init__(name, None, identifier, throws)
388        self.instance_param = instance_param
389        self.invoker = invoker
390
391    def __contains__(self, param):
392        if isinstance(param, Parameter) and param == self.instance_param:
393            return True
394        return super().__contains__(self, param)
395
396
397class Callback(Callable):
398    def __init__(self, name: str, namespace: str, ctype: T.Optional[str], throws: bool = False):
399        super().__init__(name=name, namespace=namespace, identifier=None, throws=throws)
400        self.ctype = ctype
401        self.is_fundamental = False
402
403    @property
404    def base_ctype(self):
405        if self.ctype is None:
406            return None
407        return self.ctype.replace('*', '')
408
409
410class Member(GIRElement):
411    """A member in an enumeration, error domain, or bitfield"""
412    def __init__(self, name: str, value: str, identifier: str, nick: str):
413        super().__init__(name)
414        self.value = value
415        self.identifier = identifier
416        self.nick = nick
417
418
419class Enumeration(Type):
420    """An enumeration type"""
421    def __init__(self, name: str, namespace: str, ctype: str, gtype: T.Optional[GType]):
422        super().__init__(name=name, ctype=ctype, namespace=namespace)
423        self.gtype = gtype
424        self.members: T.List[Member] = []
425        self.functions: T.List[Function] = []
426
427    def add_member(self, member: Member) -> None:
428        self.members.append(member)
429
430    def add_function(self, function: Function) -> None:
431        self.functions.append(function)
432
433    def set_members(self, members: T.List[Member]) -> None:
434        self.members.extend(members)
435
436    def set_functions(self, functions: T.List[Function]) -> None:
437        self.functions.extend(functions)
438
439    def __contains__(self, member):
440        if isinstance(member, Member):
441            return member in self.members
442        return False
443
444    def __iter__(self):
445        for member in self.members:
446            yield member
447
448
449class BitField(Enumeration):
450    """An enumeration type of bit masks"""
451    def __init__(self, name: str, namespace: str, ctype: str, gtype: T.Optional[GType]):
452        super().__init__(name, namespace, ctype, gtype)
453
454
455class ErrorDomain(Enumeration):
456    """An error domain for GError"""
457    def __init__(self, name: str, namespace: str, ctype: str, gtype: T.Optional[GType], domain: str):
458        super().__init__(name, namespace, ctype, gtype)
459        self.domain = domain
460
461
462class Property(GIRElement):
463    def __init__(self, name: str, transfer: str, target: Type, writable: bool = True, readable: bool = True, construct: bool = False,
464                 construct_only: bool = False, setter: T.Optional[str] = None, getter: T.Optional[str] = None):
465        super().__init__(name)
466        self.transfer = transfer
467        self.writable = writable
468        self.readable = readable
469        self.construct = construct
470        self.construct_only = construct_only
471        self.target = target
472        self.setter = setter
473        self.getter = getter
474
475
476class Signal(GIRElement):
477    def __init__(self, name: str, detailed: bool, when: str, action: bool = False, no_hooks: bool = False, no_recurse: bool = False):
478        super().__init__(name)
479        self.detailed = detailed
480        self.when = when
481        self.action = action
482        self.no_hooks = no_hooks
483        self.no_recurse = no_recurse
484        self.parameters: T.List[Parameter] = []
485        self.return_value: T.Optional[ReturnValue] = None
486
487    def set_parameters(self, params: T.List[Parameter]) -> None:
488        self.parameters.extend(params)
489
490    def set_return_value(self, res: ReturnValue) -> None:
491        self.return_value = res
492
493
494class Field(GIRElement):
495    """A field in a struct or union"""
496    def __init__(self, name: str, target: Type, writable: bool, readable: bool, private: bool = False, bits: int = 0):
497        super().__init__(name)
498        self.target = target
499        self.writable = writable
500        self.readable = readable
501        self.private = private
502        self.bits = bits
503
504
505class Interface(Type):
506    def __init__(self, name: str, namespace: str, ctype: str, symbol_prefix: str, gtype: GType):
507        super().__init__(name=name, ctype=ctype, namespace=namespace)
508        self.symbol_prefix = symbol_prefix
509        self.gtype = gtype
510        self.methods: T.List[Method] = []
511        self.virtual_methods: T.List[VirtualMethod] = []
512        self.properties: T.Mapping[str, Property] = {}
513        self.signals: T.Mapping[str, Signal] = {}
514        self.functions: T.List[Function] = []
515        self.fields: T.List[Field] = []
516        self.prerequisite: T.Optional[str] = None
517        self.implementations: T.List[Type] = []
518
519    @property
520    def type_struct(self) -> T.Optional[str]:
521        if self.gtype is not None:
522            return self.gtype.type_struct
523        return self.ctype
524
525    @property
526    def type_func(self) -> str:
527        return self.gtype.get_type
528
529    def set_methods(self, methods: T.List[Method]) -> None:
530        self.methods.extend(methods)
531
532    def set_virtual_methods(self, methods: T.List[VirtualMethod]) -> None:
533        self.virtual_methods.extend(methods)
534
535    def set_properties(self, properties: T.List[Property]) -> None:
536        for p in properties:
537            self.properties[p.name] = p
538
539    def set_signals(self, signals: T.List[Signal]) -> None:
540        for s in signals:
541            self.signals[s.name] = s
542
543    def set_functions(self, functions: T.List[Function]) -> None:
544        self.functions.extend(functions)
545
546    def set_fields(self, fields: T.List[Field]) -> None:
547        self.fields.extend(fields)
548
549    def set_prerequisite(self, prerequisite: str) -> None:
550        self.prerequisite = prerequisite
551
552
553class Class(Type):
554    def __init__(self, name: str, namespace: str, ctype: str, symbol_prefix: str,
555                 gtype: GType, parent: T.Optional[Type] = None,
556                 abstract: bool = False, fundamental: bool = False,
557                 ref_func: T.Optional[str] = None, unref_func: T.Optional[str] = None):
558        super().__init__(name=name, ctype=ctype, namespace=namespace)
559        self.symbol_prefix = symbol_prefix
560        self.parent = parent
561        self.abstract = abstract
562        self.fundamental = fundamental
563        self.ref_func = ref_func
564        self.unref_func = unref_func
565        self.gtype = gtype
566        self.ancestors: T.List[Type] = []
567        self.implements: T.List[Type] = []
568        self.constructors: T.List[Function] = []
569        self.methods: T.List[Method] = []
570        self.virtual_methods: T.List[VirtualMethod] = []
571        self.properties: T.Mapping[str, Property] = {}
572        self.signals: T.Mapping[str, Signal] = {}
573        self.functions: T.List[Function] = []
574        self.fields: T.List[Field] = []
575        self.callbacks: T.List[Callback] = []
576        self.descendants: T.List[Type] = []
577
578    @property
579    def type_struct(self) -> T.Optional[str]:
580        if self.gtype is not None:
581            return self.gtype.type_struct
582        return None
583
584    @property
585    def type_func(self) -> T.Optional[str]:
586        if self.gtype is not None:
587            return self.gtype.get_type
588        return self.ctype
589
590    def set_constructors(self, ctors: T.List[Function]) -> None:
591        self.constructors.extend(ctors)
592
593    def set_methods(self, methods: T.List[Method]) -> None:
594        self.methods.extend(methods)
595
596    def set_virtual_methods(self, methods: T.List[VirtualMethod]) -> None:
597        self.virtual_methods.extend(methods)
598
599    def set_properties(self, properties: T.List[Property]) -> None:
600        for p in properties:
601            self.properties[p.name] = p
602
603    def set_signals(self, signals: T.List[Signal]) -> None:
604        for s in signals:
605            self.signals[s.name] = s
606
607    def set_functions(self, functions: T.List[Function]) -> None:
608        self.functions.extend(functions)
609
610    def set_implements(self, ifaces: T.List[Type]) -> None:
611        self.implements.extend(ifaces)
612
613    def set_fields(self, fields: T.List[Field]) -> None:
614        self.fields.extend(fields)
615
616
617class Boxed(Type):
618    def __init__(self, name: str, namespace: str, symbol_prefix: str, gtype: GType):
619        super().__init__(name=name, ctype=None, namespace=namespace)
620        self.symbol_prefix = symbol_prefix
621        self.gtype = gtype
622        self.functions: T.List[Function] = []
623
624    def set_functions(self, functions: T.List[Function]) -> None:
625        self.functions.extend(functions)
626
627
628class Record(Type):
629    def __init__(self, name: str, namespace: str, ctype: str, symbol_prefix: str,
630                 gtype: T.Optional[GType] = None, struct_for: T.Optional[str] = None,
631                 disguised: bool = False):
632        super().__init__(name=name, ctype=ctype, namespace=namespace)
633        self.symbol_prefix = symbol_prefix
634        self.gtype = gtype
635        self.struct_for = struct_for
636        self.disguised = disguised
637        self.constructors: T.List[Function] = []
638        self.methods: T.List[Method] = []
639        self.functions: T.List[Function] = []
640        self.fields: T.List[Field] = []
641
642    @property
643    def type_struct(self) -> T.Optional[str]:
644        if self.gtype is not None:
645            return self.gtype.type_struct
646        return self.ctype
647
648    @property
649    def type_func(self) -> T.Optional[str]:
650        if self.gtype is not None:
651            return self.gtype.get_type
652        return None
653
654    def set_constructors(self, ctors: T.List[Function]) -> None:
655        self.constructors.extend(ctors)
656
657    def set_methods(self, methods: T.List[Method]) -> None:
658        self.methods.extend(methods)
659
660    def set_functions(self, functions: T.List[Function]) -> None:
661        self.functions.extend(functions)
662
663    def set_fields(self, fields: T.List[Field]) -> None:
664        self.fields.extend(fields)
665
666
667class Union(Type):
668    def __init__(self, name: str, namespace: str, ctype: str, symbol_prefix: str, gtype: T.Optional[GType]):
669        super().__init__(name=name, ctype=ctype, namespace=namespace)
670        self.symbol_prefix = symbol_prefix
671        self.gtype = gtype
672        self.constructors: T.List[Function] = []
673        self.methods: T.List[Method] = []
674        self.functions: T.List[Function] = []
675        self.fields: T.List[Field] = []
676
677    @property
678    def type_struct(self) -> T.Optional[str]:
679        if self.gtype is not None:
680            return self.gtype.type_struct
681        return self.ctype
682
683    @property
684    def type_func(self) -> T.Optional[str]:
685        if self.gtype is not None:
686            return self.gtype.get_type
687        return None
688
689    def set_constructors(self, ctors: T.List[Function]) -> None:
690        self.constructors.extend(ctors)
691
692    def set_methods(self, methods: T.List[Method]) -> None:
693        self.methods.extend(methods)
694
695    def set_functions(self, functions: T.List[Function]) -> None:
696        self.functions.extend(functions)
697
698    def set_fields(self, fields: T.List[Field]) -> None:
699        self.fields.extend(fields)
700
701
702class Namespace:
703    def __init__(self, name: str, version: str, identifier_prefix: T.List[str] = [], symbol_prefix: T.List[str] = []):
704        self.name = name
705        self.version = version
706
707        self._shared_libraries: T.List[str] = []
708
709        self._aliases: T.Mapping[str, Alias] = {}
710        self._bitfields: T.Mapping[str, BitField] = {}
711        self._boxeds: T.Mapping[str, Boxed] = {}
712        self._callbacks: T.Mapping[str, Callback] = {}
713        self._classes: T.Mapping[str, Class] = {}
714        self._constants: T.Mapping[str, Constant] = {}
715        self._enumerations: T.Mapping[str, Enumeration] = {}
716        self._error_domains: T.Mapping[str, ErrorDomain] = {}
717        self._functions: T.Mapping[str, Function] = {}
718        self._function_macros: T.Mapping[str, FunctionMacro] = {}
719        self._interfaces: T.Mapping[str, Interface] = {}
720        self._records: T.Mapping[str, Record] = {}
721        self._unions: T.Mapping[str, Union] = {}
722
723        self._symbols: T.Mapping[str, Type] = {}
724        self.repository: T.Optional[Repository] = None
725
726        if identifier_prefix:
727            self.identifier_prefix = identifier_prefix
728        else:
729            self.identifier_prefix = [self.name]
730        if symbol_prefix:
731            self.symbol_prefix = symbol_prefix
732        else:
733            self.symbol_prefix = [self.name.lower()]
734
735    def __str__(self):
736        return f"{self.name}-{self.version}"
737
738    def add_shared_libraries(self, libs: T.List[str]) -> None:
739        self._shared_libraries.extend(libs)
740
741    def get_shared_libraries(self) -> T.List[str]:
742        return self._shared_libraries
743
744    def add_alias(self, alias: Alias) -> None:
745        self._aliases[alias.name] = alias
746
747    def add_enumeration(self, enum: Enumeration) -> None:
748        self._enumerations[enum.name] = enum
749
750    def add_error_domain(self, domain: ErrorDomain) -> None:
751        self._error_domains[domain.name] = domain
752
753    def add_class(self, cls: Class) -> None:
754        self._classes[cls.name] = cls
755
756    def add_constant(self, constant: Constant) -> None:
757        self._constants[constant.name] = constant
758
759    def add_interface(self, interface: Interface) -> None:
760        self._interfaces[interface.name] = interface
761
762    def add_boxed(self, boxed: Boxed) -> None:
763        self._boxeds[boxed.name] = boxed
764
765    def add_record(self, record: Record) -> None:
766        self._records[record.name] = record
767
768    def add_union(self, union: Union) -> None:
769        self._unions[union.name] = union
770
771    def add_function(self, function: Function) -> None:
772        self._functions[function.name] = function
773
774    def add_bitfield(self, bitfield: BitField) -> None:
775        self._bitfields[bitfield.name] = bitfield
776
777    def add_function_macro(self, function: FunctionMacro) -> None:
778        self._function_macros[function.name] = function
779
780    def add_callback(self, callback: Callback) -> None:
781        self._callbacks[callback.name] = callback
782
783    def get_classes(self) -> T.List[Class]:
784        return self._classes.values()
785
786    def get_constants(self) -> T.List[Constant]:
787        return self._constants.values()
788
789    def get_enumerations(self) -> T.List[Enumeration]:
790        return self._enumerations.values()
791
792    def get_error_domains(self) -> T.List[ErrorDomain]:
793        return self._error_domains.values()
794
795    def get_aliases(self) -> T.List[Alias]:
796        return self._aliases.values()
797
798    def get_interfaces(self) -> T.List[Interface]:
799        return self._interfaces.values()
800
801    def get_boxeds(self) -> T.List[Boxed]:
802        return self._boxeds.values()
803
804    def get_records(self) -> T.List[Record]:
805        return self._records.values()
806
807    def get_effective_records(self) -> T.List[Record]:
808        def is_effective(r):
809            if "Private" in r.name and r.disguised:
810                return False
811            if r.struct_for is not None:
812                return False
813            return True
814
815        return [x for x in self._records.values() if is_effective(x)]
816
817    def get_unions(self) -> T.List[Union]:
818        return self._unions.values()
819
820    def get_functions(self) -> T.List[Function]:
821        return self._functions.values()
822
823    def get_bitfields(self) -> T.List[BitField]:
824        return self._bitfields.values()
825
826    def get_function_macros(self) -> T.List[FunctionMacro]:
827        return self._function_macros.values()
828
829    def get_effective_function_macros(self) -> T.List[FunctionMacro]:
830        def is_effective(f, ns):
831            # Lower-case identifiers are an automatic pass
832            if f.name.islower():
833                return True
834            # Try to eliminate the GObject type macros from the pool
835            t = f.name.split('_')
836            # Skip "is-a" macros
837            if 'IS' in t:
838                return False
839            # Skip "get class/iface" macros
840            if 'GET' in t:
841                return False
842            # Re-assemble into what most likely is a type name
843            s = "".join([x.capitalize() if len(x) > 2 else x for x in t])
844            # Skip "cast" macros
845            if ns.find_class(s) is not None:
846                return False
847            if ns.find_interface(s) is not None:
848                return False
849            if ns.find_record(s) is not None:
850                return False
851            # Anything that survived at this point is likely a valid function
852            # macro
853            return True
854
855        return [x for x in self._function_macros.values() if is_effective(x, self)]
856
857    def get_callbacks(self) -> T.List[Callback]:
858        return self._callbacks.values()
859
860    def find_class(self, cls: str) -> T.Optional[Class]:
861        return self._classes.get(cls)
862
863    def find_record(self, record: str) -> T.Optional[Record]:
864        return self._records.get(record)
865
866    def find_interface(self, iface: str) -> T.Optional[Interface]:
867        return self._interfaces.get(iface)
868
869    def find_union(self, union: str) -> T.Optional[Union]:
870        return self._unions.get(union)
871
872    def find_enumeration(self, enum: str) -> T.Optional[Enumeration]:
873        return self._enumerations.get(enum)
874
875    def find_bitfield(self, bitfield: str) -> T.Optional[BitField]:
876        return self._bitfields.get(bitfield)
877
878    def find_error_domain(self, domain: str) -> T.Optional[ErrorDomain]:
879        return self._error_domains.get(domain)
880
881    def find_alias(self, alias: str) -> T.Optional[Alias]:
882        return self._aliases.get(alias)
883
884    def find_function(self, func: str) -> T.Optional[Function]:
885        if func in self._functions:
886            return self._functions.get(func)
887        if func in self._function_macros:
888            return self._function_macros.get(func)
889        return None
890
891    def find_real_type(self, name: str) -> T.Optional[Type]:
892        if name in self._aliases:
893            return self._aliases[name]
894        if name in self._bitfields:
895            return self._bitfields[name]
896        if name in self._callbacks:
897            return self._callbacks[name]
898        if name in self._constants:
899            return self._constants[name]
900        if name in self._enumerations:
901            return self._enumerations[name]
902        if name in self._error_domains:
903            return self._error_domains[name]
904        if name in self._classes:
905            return self._classes[name]
906        if name in self._interfaces:
907            return self._interfaces[name]
908        if name in self._records:
909            return self._records[name]
910        if name in self._unions:
911            return self._unions[name]
912        return None
913
914    def find_symbol(self, name: str) -> T.Optional[Type]:
915        return self._symbols.get(name)
916
917    def find_prerequisite_type(self, name: str) -> T.Optional[Type]:
918        if name in self._classes:
919            return self._classes[name]
920        if name in self._interfaces:
921            return self._interfaces[name]
922        return None
923
924
925class Repository:
926    def __init__(self):
927        self.includes: T.Mapping[str, Repository] = {}
928        self.packages: T.List[Package] = []
929        self.c_includes: T.List[CInclude] = []
930        self.types: T.Mapping[str, T.List[Type]] = {}
931        self._namespaces: T.List[Namespace] = []
932        self.girfile: T.Optional[str] = None
933
934    def add_namespace(self, ns: Namespace) -> None:
935        self._namespaces.append(ns)
936        ns.repository = self
937
938    def get_namespace(self, ns: str) -> T.Optional[Namespace]:
939        for namespace in self._namespaces:
940            if namespace.name == ns:
941                return namespace
942        return None
943
944    def find_included_namespace(self, ns: str) -> T.Optional[Namespace]:
945        for repo_name in self.includes:
946            repo = self.includes[repo_name]
947            if repo.namespace.name == ns:
948                return repo.namespace
949        return None
950
951    def _lookup_type(self, name: str) -> T.Optional[Type]:
952        types = self.types.get(name)
953        if types is None:
954            return None
955        for t in types:
956            if t.resolved:
957                return t
958        return types[0]
959
960    def resolve_empty_ctypes(self, seen_types: T.Mapping[str, T.List[Type]]) -> None:
961        for fqtn in seen_types:
962            types = seen_types[fqtn]
963            resolved_types = [t for t in types if t.resolved]
964            if len(resolved_types) == 0:
965                ns, name = fqtn.split('.', 1)
966                backstop = f"{self.namespace.identifier_prefix[0]}{name}"
967                resolved_types.append(Type(fqtn, backstop))
968            self.types[fqtn] = resolved_types
969            log.debug(f"Type: {fqtn}: {resolved_types}")
970
971    def resolve_interface_requires(self) -> None:
972        def find_prerequisite_type(includes, ns, name):
973            repository = includes.get(ns)
974            if repository is None:
975                return None
976            prereq = repository.namespace.find_prerequisite_type(name)
977            # If the prerequisite type is unqualified, then we qualify it here
978            if '.' not in prereq.name:
979                prereq.name = f"{repository.namespace.name}.{prereq.name}"
980            return prereq
981
982        ifaces = self.namespace.get_interfaces()
983        for iface in ifaces:
984            if iface.prerequisite is None:
985                continue
986            prerequisite = None
987            if '.' in iface.prerequisite.name:
988                ns, name = iface.prerequisite.name.split('.', 1)
989                if ns == self.namespace.name:
990                    prerequisite = self.namespace.find_prerequisite_type(name)
991                else:
992                    prerequisite = find_prerequisite_type(self.includes, ns, name)
993            else:
994                prerequisite = self.namespace.find_prerequisite_type(iface.prerequisite.name)
995            if prerequisite is not None:
996                if prerequisite.ctype is None:
997                    if '.' not in prerequisite.name:
998                        name = f"{self.namespace.name}.{prerequisite.name}"
999                    else:
1000                        name = prerequisite.name
1001                    t = self._lookup_type(name)
1002                    if t is not None:
1003                        prerequisite.ctype = t.ctype
1004                    else:
1005                        # This is kind of a kludge, but apparently we can get into
1006                        # class definitions missing a c:type; if that happens, we
1007                        # take the identifier prefix of the namespace and append the
1008                        # class name, because that's the inverse of how g-ir-scanner
1009                        # determines the class name
1010                        prerequisite.ctype = f"{self.namespace.identifier_prefix[0]}{prerequisite.name}"
1011                iface.prerequisite = prerequisite
1012                log.debug(f"Prerequisite type for interface {iface}: {iface.prerequisite}")
1013
1014    def resolve_class_ctype(self) -> None:
1015        classes = self.namespace.get_classes()
1016        for cls in classes:
1017            if cls.ctype is None:
1018                if '.' not in cls.name:
1019                    name = f"{self.namespace.name}.{cls.name}"
1020                else:
1021                    name = cls.name
1022                t = self._lookup_type(name)
1023                if t is not None:
1024                    cls.ctype = t.base_ctype
1025                else:
1026                    # This is kind of a kludge, but apparently we can get into
1027                    # class definitions missing a c:type; if that happens, we
1028                    # take the identifier prefix of the namespace and append the
1029                    # class name, because that's the inverse of how g-ir-scanner
1030                    # determines the class name
1031                    cls.ctype = f"{self.namespace.identifier_prefix[0]}{cls.name}"
1032                log.debug(f"Updated C type for {cls}")
1033
1034    def resolve_class_implements(self) -> None:
1035        def find_interface_type(includes, ns, name):
1036            repository = includes.get(ns)
1037            if repository is None:
1038                return None
1039            iface = repository.namespace.find_interface(name)
1040            # If the interface type is unqualified, then we qualify it here
1041            if '.' not in iface.name:
1042                iface.name = f"{repository.namespace.name}.{iface.name}"
1043            return iface
1044
1045        classes = self.namespace.get_classes()
1046        for cls in classes:
1047            if cls.implements is None:
1048                continue
1049            implements = cls.implements
1050            cls.implements = []
1051            for iface in implements:
1052                if '.' in iface.name:
1053                    ns, name = iface.name.split('.', 1)
1054                    if ns == self.namespace.name:
1055                        iface_type = self.namespace.find_interface(name)
1056                    else:
1057                        iface_type = find_interface_type(self.includes, ns, name)
1058                else:
1059                    iface_type = self.namespace.find_interface(iface.name)
1060                if iface_type is not None:
1061                    if iface_type.ctype is None:
1062                        t = self._lookup_type(iface_type.name)
1063                        iface_type.ctype = t.ctype
1064                    cls.implements.append(iface_type)
1065            log.debug(f"Interfaces implemented by {cls}: {cls.implements}")
1066
1067    def resolve_class_ancestors(self) -> None:
1068        def find_parent_class(includes, ns, name):
1069            repository = includes.get(ns)
1070            if repository is None:
1071                return None
1072            parent_class = repository.namespace.find_class(name)
1073            # If the parent type is unqualified, then we qualify it here
1074            if '.' not in parent_class.name:
1075                parent_class.name = f"{repository.namespace.name}.{parent_class.name}"
1076            return parent_class
1077
1078        classes = self.namespace.get_classes()
1079        for cls in classes:
1080            if cls.parent is None:
1081                continue
1082            ancestors = []
1083            parent = cls.parent
1084            while parent is not None:
1085                if '.' in parent.name:
1086                    ns, name = parent.name.split('.')
1087                    if ns == self.namespace.name:
1088                        real_parent = self.namespace.find_class(name)
1089                    else:
1090                        real_parent = find_parent_class(self.includes, ns, name)
1091                else:
1092                    real_parent = self.namespace.find_class(parent.name)
1093                if real_parent is None:
1094                    break
1095                if real_parent.parent is not None and real_parent.parent.name == parent.name:
1096                    log.warning(f"Found a loop in the ancestors for {cls}: {real_parent} matches {parent}")
1097                    break
1098                if real_parent.ctype is None:
1099                    log.debug(f"Looking up C type for {parent.fqtn}")
1100                    t = self._lookup_type(parent.name)
1101                    real_parent.ctype = t.ctype
1102                log.debug(f"Adding ancestor {real_parent} for {cls}")
1103                ancestors.append(real_parent)
1104                parent = real_parent.parent
1105            cls.ancestors = ancestors
1106            cls.parent = ancestors[0]
1107            log.debug(f"Ancestors for {cls}: parent: {cls.parent}, ancestors: {cls.ancestors}")
1108
1109    def resolve_class_descendants(self) -> None:
1110        seen_parents = {}
1111        for cls in self.namespace.get_classes():
1112            if cls.parent is not None:
1113                seen_parents.setdefault(cls.parent.name, []).append(cls)
1114        for name, descendants in seen_parents.items():
1115            if name in self.namespace._classes:
1116                self.namespace._classes[name].descendants = descendants
1117
1118    def resolve_moved_to(self) -> None:
1119        functions = list(self.namespace.get_functions())
1120        old_len = len(functions)
1121        for func in functions[:]:
1122            if func.moved_to is None:
1123                continue
1124            moved_type, moved_func_name = func.moved_to.split('.')
1125            real_type = self.namespace.find_real_type(moved_type)
1126            if real_type is None:
1127                continue
1128            self.namespace._functions.pop(func.name)    # XXX: Add accessor
1129        new_len = len(self.namespace._functions)
1130        diff = old_len - new_len
1131        log.debug(f"Removed {old_len} - {new_len} functions: {diff}")
1132
1133    def resolve_symbols(self) -> None:
1134        symbols: T.Mapping[str, Type] = {}
1135        for func in self.namespace.get_functions():
1136            symbols[func.identifier] = func
1137        for func in self.namespace.get_function_macros():
1138            symbols[func.identifier] = func
1139        for cls in self.namespace.get_classes():
1140            for m in cls.constructors:
1141                symbols[m.identifier] = cls
1142            for m in cls.methods:
1143                symbols[m.identifier] = cls
1144            for m in cls.functions:
1145                symbols[m.identifier] = cls
1146        for iface in self.namespace.get_interfaces():
1147            for m in iface.methods:
1148                symbols[m.identifier] = iface
1149            for m in iface.functions:
1150                symbols[m.identifier] = iface
1151        for record in self.namespace.get_records():
1152            for m in record.constructors:
1153                symbols[m.identifier] = record
1154            for m in record.methods:
1155                symbols[m.identifier] = record
1156            for m in record.functions:
1157                symbols[m.identifier] = record
1158        for union in self.namespace.get_unions():
1159            for m in union.constructors:
1160                symbols[m.identifier] = union
1161            for m in union.methods:
1162                symbols[m.identifier] = union
1163            for m in union.functions:
1164                symbols[m.identifier] = union
1165        self.namespace._symbols = symbols
1166
1167    def resolve_interface_implementations(self) -> None:
1168        seen_impls = {}
1169        for iface in self.namespace.get_interfaces():
1170            for cls in self.namespace.get_classes():
1171                if cls.implements is None:
1172                    continue
1173                if iface in cls.implements:
1174                    seen_impls.setdefault(iface.name, []).append(cls)
1175        for iface, seen in seen_impls.items():
1176            if iface in self.namespace._interfaces:
1177                self.namespace._interfaces[iface].implementations = seen
1178
1179    def get_class_hierarchy(self, root=None):
1180        flat_tree = []
1181        seen_types = {}
1182
1183        def window(iterable, size=2):
1184            i = iter(iterable)
1185            win = []
1186            for e in range(0, size):
1187                win.append(next(i))
1188            yield win
1189            for e in i:
1190                win = win[1:] + [e]
1191                yield win
1192
1193        for cls in self.namespace.get_classes():
1194            if cls.parent is None:
1195                flat_tree.append((cls.name, None))
1196                continue
1197
1198            if len(cls.ancestors) < 2:
1199                flat_tree.append((cls.name, cls.ancestors[0].name))
1200            else:
1201                flat_tree.append((cls.name, cls.ancestors[0].name))
1202                for chunk in window(cls.ancestors, size=2):
1203                    if chunk[0].name in seen_types:
1204                        continue
1205                    if len(chunk) == 2:
1206                        flat_tree.append((chunk[0].name, chunk[1].name))
1207                    else:
1208                        flat_tree.append((chunk[0].name, None))
1209                    seen_types[chunk[0].name] = 1
1210
1211        def subtree(cls, rel):
1212            return {
1213                v: subtree(v, rel)
1214                for v in [x[0] for x in rel if x[1] == cls]
1215            }
1216
1217        return subtree(root, flat_tree)
1218
1219    @property
1220    def namespace(self) -> T.Optional[Namespace]:
1221        return self._namespaces[0]
1222
1223    def find_type(self, name: str, ns: T.Optional[str] = None) -> T.Optional[T.Tuple[Namespace, Type]]:
1224        if ns is None or self.namespace.name == ns:
1225            res = self.namespace.find_real_type(name)
1226            if res is not None:
1227                return (self.namespace, res)
1228        for repo in self.includes.values():
1229            if ns is not None and ns != repo.namespace.name:
1230                continue
1231            res = repo.namespace.find_real_type(name)
1232            if res is not None:
1233                return (repo.namespace, res)
1234        return None
1235
1236    def find_symbol(self, name: str) -> T.Optional[T.Tuple[Namespace, Type]]:
1237        log.debug(f"Looking for symbol {name} in current namespace {self.namespace.name}")
1238        res = self.namespace.find_symbol(name)
1239        if res is not None:
1240            return (self.namespace, res)
1241        for repo in self.includes.values():
1242            log.debug(f"Looking for symbol {name} in namespace {repo.namespace.name}")
1243            res = repo.namespace.find_symbol(name)
1244            if res is not None:
1245                return (repo.namespace, res)
1246        return None
1247
1248    def find_class(self, name: str, ns: T.Optional[str] = None) -> T.Optional[T.Tuple[Namespace, Type]]:
1249        if ns is None or self.namespace.name == ns:
1250            res = self.namespace.find_class(name)
1251            if res is not None:
1252                return (self.namespace, res)
1253        for repo in self.includes.values():
1254            if ns is not None and ns != repo.namespace.name:
1255                continue
1256            res = repo.namespace.find_class(name)
1257            if res is not None:
1258                return (repo.namespace, res)
1259        return None
1260
1261    def find_interface(self, name: str, ns: T.Optional[str] = None) -> T.Optional[T.Tuple[Namespace, Type]]:
1262        if ns is None or self.namespace.name == ns:
1263            res = self.namespace.find_interface(name)
1264            if res is not None:
1265                return (self.namespace, res)
1266        for repo in self.includes.values():
1267            if ns is not None and ns != repo.namespace.name:
1268                continue
1269            res = repo.namespace.find_interface(name)
1270            if res is not None:
1271                return (repo.namespace, res)
1272        return None
1273