1e5c31af7Sopenharmony_ci#!/usr/bin/python3 -i
2e5c31af7Sopenharmony_ci#
3e5c31af7Sopenharmony_ci# Copyright 2013-2024 The Khronos Group Inc.
4e5c31af7Sopenharmony_ci#
5e5c31af7Sopenharmony_ci# SPDX-License-Identifier: Apache-2.0
6e5c31af7Sopenharmony_ci
7e5c31af7Sopenharmony_ciimport re
8e5c31af7Sopenharmony_cifrom collections import OrderedDict, namedtuple
9e5c31af7Sopenharmony_cifrom functools import reduce
10e5c31af7Sopenharmony_cifrom pathlib import Path
11e5c31af7Sopenharmony_ci
12e5c31af7Sopenharmony_cifrom spec_tools.conventions import ProseListFormats as plf
13e5c31af7Sopenharmony_cifrom generator import OutputGenerator, write
14e5c31af7Sopenharmony_cifrom spec_tools.attributes import ExternSyncEntry, LengthEntry
15e5c31af7Sopenharmony_cifrom spec_tools.util import (findNamedElem, findNamedObject, findTypedElem,
16e5c31af7Sopenharmony_ci                             getElemName, getElemType)
17e5c31af7Sopenharmony_cifrom spec_tools.validity import ValidityCollection, ValidityEntry
18e5c31af7Sopenharmony_ci
19e5c31af7Sopenharmony_ci
20e5c31af7Sopenharmony_ciclass UnhandledCaseError(RuntimeError):
21e5c31af7Sopenharmony_ci    def __init__(self, msg=None):
22e5c31af7Sopenharmony_ci        if msg:
23e5c31af7Sopenharmony_ci            super().__init__('Got a case in the validity generator that we have not explicitly handled: ' + msg)
24e5c31af7Sopenharmony_ci        else:
25e5c31af7Sopenharmony_ci            super().__init__('Got a case in the validity generator that we have not explicitly handled.')
26e5c31af7Sopenharmony_ci
27e5c31af7Sopenharmony_ci
28e5c31af7Sopenharmony_cidef _genericIterateIntersection(a, b):
29e5c31af7Sopenharmony_ci    """Iterate through all elements in a that are also in b.
30e5c31af7Sopenharmony_ci
31e5c31af7Sopenharmony_ci    Somewhat like a set's intersection(),
32e5c31af7Sopenharmony_ci    but not type-specific so it can work with OrderedDicts, etc.
33e5c31af7Sopenharmony_ci    It also returns a generator instead of a set,
34e5c31af7Sopenharmony_ci    so you can pick a preferred container type,
35e5c31af7Sopenharmony_ci    if any.
36e5c31af7Sopenharmony_ci    """
37e5c31af7Sopenharmony_ci    return (x for x in a if x in b)
38e5c31af7Sopenharmony_ci
39e5c31af7Sopenharmony_ci
40e5c31af7Sopenharmony_cidef _make_ordered_dict(gen):
41e5c31af7Sopenharmony_ci    """Make an ordered dict (with None as the values) from a generator."""
42e5c31af7Sopenharmony_ci    return OrderedDict(((x, None) for x in gen))
43e5c31af7Sopenharmony_ci
44e5c31af7Sopenharmony_ci
45e5c31af7Sopenharmony_cidef _orderedDictIntersection(a, b):
46e5c31af7Sopenharmony_ci    return _make_ordered_dict(_genericIterateIntersection(a, b))
47e5c31af7Sopenharmony_ci
48e5c31af7Sopenharmony_ci
49e5c31af7Sopenharmony_cidef _genericIsDisjoint(a, b):
50e5c31af7Sopenharmony_ci    """Return true if nothing in a is also in b.
51e5c31af7Sopenharmony_ci
52e5c31af7Sopenharmony_ci    Like a set's is_disjoint(),
53e5c31af7Sopenharmony_ci    but not type-specific so it can work with OrderedDicts, etc.
54e5c31af7Sopenharmony_ci    """
55e5c31af7Sopenharmony_ci    for _ in _genericIterateIntersection(a, b):
56e5c31af7Sopenharmony_ci        return False
57e5c31af7Sopenharmony_ci    # if we never enter the loop...
58e5c31af7Sopenharmony_ci    return True
59e5c31af7Sopenharmony_ci
60e5c31af7Sopenharmony_ci
61e5c31af7Sopenharmony_ciclass ValidityOutputGenerator(OutputGenerator):
62e5c31af7Sopenharmony_ci    """ValidityOutputGenerator - subclass of OutputGenerator.
63e5c31af7Sopenharmony_ci
64e5c31af7Sopenharmony_ci    Generates AsciiDoc includes of valid usage information, for reference
65e5c31af7Sopenharmony_ci    pages and the specification. Similar to DocOutputGenerator.
66e5c31af7Sopenharmony_ci
67e5c31af7Sopenharmony_ci    ---- methods ----
68e5c31af7Sopenharmony_ci    ValidityOutputGenerator(errFile, warnFile, diagFile) - args as for
69e5c31af7Sopenharmony_ci    OutputGenerator. Defines additional internal state.
70e5c31af7Sopenharmony_ci    ---- methods overriding base class ----
71e5c31af7Sopenharmony_ci    beginFile(genOpts)
72e5c31af7Sopenharmony_ci    endFile()
73e5c31af7Sopenharmony_ci    beginFeature(interface, emit)
74e5c31af7Sopenharmony_ci    endFeature()
75e5c31af7Sopenharmony_ci    genCmd(cmdinfo)
76e5c31af7Sopenharmony_ci    """
77e5c31af7Sopenharmony_ci
78e5c31af7Sopenharmony_ci    def __init__(self, *args, **kwargs):
79e5c31af7Sopenharmony_ci        super().__init__(*args, **kwargs)
80e5c31af7Sopenharmony_ci
81e5c31af7Sopenharmony_ci        self.currentExtension = ''
82e5c31af7Sopenharmony_ci
83e5c31af7Sopenharmony_ci        # Tracks whether we are tracing operations
84e5c31af7Sopenharmony_ci        self.trace = False
85e5c31af7Sopenharmony_ci
86e5c31af7Sopenharmony_ci    @property
87e5c31af7Sopenharmony_ci    def null(self):
88e5c31af7Sopenharmony_ci        """Preferred spelling of NULL.
89e5c31af7Sopenharmony_ci
90e5c31af7Sopenharmony_ci        Delegates to the object implementing ConventionsBase.
91e5c31af7Sopenharmony_ci        """
92e5c31af7Sopenharmony_ci        return self.conventions.null
93e5c31af7Sopenharmony_ci
94e5c31af7Sopenharmony_ci    @property
95e5c31af7Sopenharmony_ci    def structtype_member_name(self):
96e5c31af7Sopenharmony_ci        """Return name of the structure type member.
97e5c31af7Sopenharmony_ci
98e5c31af7Sopenharmony_ci        Delegates to the object implementing ConventionsBase.
99e5c31af7Sopenharmony_ci        """
100e5c31af7Sopenharmony_ci        return self.conventions.structtype_member_name
101e5c31af7Sopenharmony_ci
102e5c31af7Sopenharmony_ci    @property
103e5c31af7Sopenharmony_ci    def nextpointer_member_name(self):
104e5c31af7Sopenharmony_ci        """Return name of the structure pointer chain member.
105e5c31af7Sopenharmony_ci
106e5c31af7Sopenharmony_ci        Delegates to the object implementing ConventionsBase.
107e5c31af7Sopenharmony_ci        """
108e5c31af7Sopenharmony_ci        return self.conventions.nextpointer_member_name
109e5c31af7Sopenharmony_ci
110e5c31af7Sopenharmony_ci    def makeProseList(self, elements, fmt=plf.AND,
111e5c31af7Sopenharmony_ci                      comma_for_two_elts=False, *args, **kwargs):
112e5c31af7Sopenharmony_ci        """Make a (comma-separated) list for use in prose.
113e5c31af7Sopenharmony_ci
114e5c31af7Sopenharmony_ci        Adds a connective (by default, 'and')
115e5c31af7Sopenharmony_ci        before the last element if there are more than 1.
116e5c31af7Sopenharmony_ci
117e5c31af7Sopenharmony_ci        Optionally adds a quantifier (like 'any') before a list of 2 or more,
118e5c31af7Sopenharmony_ci        if specified by fmt.
119e5c31af7Sopenharmony_ci
120e5c31af7Sopenharmony_ci        Delegates to the object implementing ConventionsBase.
121e5c31af7Sopenharmony_ci        """
122e5c31af7Sopenharmony_ci        if not elements:
123e5c31af7Sopenharmony_ci            raise ValueError(
124e5c31af7Sopenharmony_ci                'Cannot pass an empty list if you are trying to make a prose list.')
125e5c31af7Sopenharmony_ci        return self.conventions.makeProseList(elements,
126e5c31af7Sopenharmony_ci                                              fmt,
127e5c31af7Sopenharmony_ci                                              with_verb=False,
128e5c31af7Sopenharmony_ci                                              comma_for_two_elts=comma_for_two_elts,
129e5c31af7Sopenharmony_ci                                              *args, **kwargs)
130e5c31af7Sopenharmony_ci
131e5c31af7Sopenharmony_ci    def makeProseListIs(self, elements, fmt=plf.AND,
132e5c31af7Sopenharmony_ci                        comma_for_two_elts=False, *args, **kwargs):
133e5c31af7Sopenharmony_ci        """Make a (comma-separated) list for use in prose, followed by either 'is' or 'are' as appropriate.
134e5c31af7Sopenharmony_ci
135e5c31af7Sopenharmony_ci        Adds a connective (by default, 'and')
136e5c31af7Sopenharmony_ci        before the last element if there are more than 1.
137e5c31af7Sopenharmony_ci
138e5c31af7Sopenharmony_ci        Optionally adds a quantifier (like 'any') before a list of 2 or more,
139e5c31af7Sopenharmony_ci        if specified by fmt.
140e5c31af7Sopenharmony_ci
141e5c31af7Sopenharmony_ci        Delegates to the object implementing ConventionsBase.
142e5c31af7Sopenharmony_ci        """
143e5c31af7Sopenharmony_ci        if not elements:
144e5c31af7Sopenharmony_ci            raise ValueError(
145e5c31af7Sopenharmony_ci                'Cannot pass an empty list if you are trying to make a prose list.')
146e5c31af7Sopenharmony_ci        return self.conventions.makeProseList(elements,
147e5c31af7Sopenharmony_ci                                              fmt,
148e5c31af7Sopenharmony_ci                                              with_verb=True,
149e5c31af7Sopenharmony_ci                                              comma_for_two_elts=comma_for_two_elts,
150e5c31af7Sopenharmony_ci                                              *args, **kwargs)
151e5c31af7Sopenharmony_ci
152e5c31af7Sopenharmony_ci    def makeValidityCollection(self, entity_name):
153e5c31af7Sopenharmony_ci        """Create a ValidityCollection object, passing along our Conventions."""
154e5c31af7Sopenharmony_ci        return ValidityCollection(entity_name, self.conventions)
155e5c31af7Sopenharmony_ci
156e5c31af7Sopenharmony_ci    def beginFile(self, genOpts):
157e5c31af7Sopenharmony_ci        if not genOpts.conventions:
158e5c31af7Sopenharmony_ci            raise RuntimeError(
159e5c31af7Sopenharmony_ci                'Must specify conventions object to generator options')
160e5c31af7Sopenharmony_ci        self.conventions = genOpts.conventions
161e5c31af7Sopenharmony_ci        # Vulkan says 'must: be a valid pointer' a lot, OpenXR just says
162e5c31af7Sopenharmony_ci        # 'must: be a pointer'.
163e5c31af7Sopenharmony_ci        self.valid_pointer_text = ' '.join(
164e5c31af7Sopenharmony_ci            (x for x in (self.conventions.valid_pointer_prefix, 'pointer') if x))
165e5c31af7Sopenharmony_ci        OutputGenerator.beginFile(self, genOpts)
166e5c31af7Sopenharmony_ci
167e5c31af7Sopenharmony_ci    def endFile(self):
168e5c31af7Sopenharmony_ci        OutputGenerator.endFile(self)
169e5c31af7Sopenharmony_ci
170e5c31af7Sopenharmony_ci    def beginFeature(self, interface, emit):
171e5c31af7Sopenharmony_ci        # Start processing in superclass
172e5c31af7Sopenharmony_ci        OutputGenerator.beginFeature(self, interface, emit)
173e5c31af7Sopenharmony_ci        self.currentExtension = interface.get('name')
174e5c31af7Sopenharmony_ci
175e5c31af7Sopenharmony_ci    def endFeature(self):
176e5c31af7Sopenharmony_ci        # Finish processing in superclass
177e5c31af7Sopenharmony_ci        OutputGenerator.endFeature(self)
178e5c31af7Sopenharmony_ci
179e5c31af7Sopenharmony_ci    @property
180e5c31af7Sopenharmony_ci    def struct_macro(self):
181e5c31af7Sopenharmony_ci        """Get the appropriate format macro for a structure."""
182e5c31af7Sopenharmony_ci        # delegate to conventions
183e5c31af7Sopenharmony_ci        return self.conventions.struct_macro
184e5c31af7Sopenharmony_ci
185e5c31af7Sopenharmony_ci    def makeStructName(self, name):
186e5c31af7Sopenharmony_ci        """Prepend the appropriate format macro for a structure to a structure type name."""
187e5c31af7Sopenharmony_ci        # delegate to conventions
188e5c31af7Sopenharmony_ci        return self.conventions.makeStructName(name)
189e5c31af7Sopenharmony_ci
190e5c31af7Sopenharmony_ci    def makeParameterName(self, name):
191e5c31af7Sopenharmony_ci        """Prepend the appropriate format macro for a parameter/member to a parameter name."""
192e5c31af7Sopenharmony_ci        return 'pname:' + name
193e5c31af7Sopenharmony_ci
194e5c31af7Sopenharmony_ci    def makeBaseTypeName(self, name):
195e5c31af7Sopenharmony_ci        """Prepend the appropriate format macro for a 'base type' to a type name."""
196e5c31af7Sopenharmony_ci        return 'basetype:' + name
197e5c31af7Sopenharmony_ci
198e5c31af7Sopenharmony_ci    def makeEnumerationName(self, name):
199e5c31af7Sopenharmony_ci        """Prepend the appropriate format macro for an enumeration type to a enum type name."""
200e5c31af7Sopenharmony_ci        return 'elink:' + name
201e5c31af7Sopenharmony_ci
202e5c31af7Sopenharmony_ci    def makeFlagsName(self, name):
203e5c31af7Sopenharmony_ci        """Prepend the appropriate format macro for a flags type to a flags type name."""
204e5c31af7Sopenharmony_ci        return 'tlink:' + name
205e5c31af7Sopenharmony_ci
206e5c31af7Sopenharmony_ci    def makeFuncPointerName(self, name):
207e5c31af7Sopenharmony_ci        """Prepend the appropriate format macro for a function pointer type to a type name."""
208e5c31af7Sopenharmony_ci        return 'tlink:' + name
209e5c31af7Sopenharmony_ci
210e5c31af7Sopenharmony_ci    def makeExternalTypeName(self, name):
211e5c31af7Sopenharmony_ci        """Prepend the appropriate format macro for an external type like uint32_t to a type name."""
212e5c31af7Sopenharmony_ci        # delegate to conventions
213e5c31af7Sopenharmony_ci        return self.conventions.makeExternalTypeName(name)
214e5c31af7Sopenharmony_ci
215e5c31af7Sopenharmony_ci    def makeEnumerantName(self, name):
216e5c31af7Sopenharmony_ci        """Prepend the appropriate format macro for an enumerate (value) to a enum value name."""
217e5c31af7Sopenharmony_ci        return 'ename:' + name
218e5c31af7Sopenharmony_ci
219e5c31af7Sopenharmony_ci    def writeInclude(self, directory, basename, validity: ValidityCollection,
220e5c31af7Sopenharmony_ci                     threadsafety, commandpropertiesentry=None,
221e5c31af7Sopenharmony_ci                     successcodes=None, errorcodes=None):
222e5c31af7Sopenharmony_ci        """Generate an include file.
223e5c31af7Sopenharmony_ci
224e5c31af7Sopenharmony_ci        directory - subdirectory to put file in (absolute or relative pathname)
225e5c31af7Sopenharmony_ci        basename - base name of the file
226e5c31af7Sopenharmony_ci        validity - ValidityCollection to write.
227e5c31af7Sopenharmony_ci        threadsafety - List (may be empty) of thread safety statements to write.
228e5c31af7Sopenharmony_ci        successcodes - Optional success codes to document.
229e5c31af7Sopenharmony_ci        errorcodes - Optional error codes to document.
230e5c31af7Sopenharmony_ci        """
231e5c31af7Sopenharmony_ci        # Create subdirectory, if needed
232e5c31af7Sopenharmony_ci        directory = Path(directory)
233e5c31af7Sopenharmony_ci        if not directory.is_absolute():
234e5c31af7Sopenharmony_ci            directory = Path(self.genOpts.directory) / directory
235e5c31af7Sopenharmony_ci        self.makeDir(str(directory))
236e5c31af7Sopenharmony_ci
237e5c31af7Sopenharmony_ci        # Create validity file
238e5c31af7Sopenharmony_ci        filename = str(directory / f'{basename}{self.file_suffix}')
239e5c31af7Sopenharmony_ci        self.logMsg('diag', '# Generating include file:', filename)
240e5c31af7Sopenharmony_ci
241e5c31af7Sopenharmony_ci        with open(filename, 'w', encoding='utf-8') as fp:
242e5c31af7Sopenharmony_ci            write(self.conventions.warning_comment, file=fp)
243e5c31af7Sopenharmony_ci
244e5c31af7Sopenharmony_ci            # Valid Usage
245e5c31af7Sopenharmony_ci            if validity:
246e5c31af7Sopenharmony_ci                write('.Valid Usage (Implicit)', file=fp)
247e5c31af7Sopenharmony_ci                write('****', file=fp)
248e5c31af7Sopenharmony_ci                write(validity, file=fp, end='')
249e5c31af7Sopenharmony_ci                write('****', file=fp)
250e5c31af7Sopenharmony_ci                write('', file=fp)
251e5c31af7Sopenharmony_ci
252e5c31af7Sopenharmony_ci            # Host Synchronization
253e5c31af7Sopenharmony_ci            if threadsafety:
254e5c31af7Sopenharmony_ci                # The heading of this block differs between projects, so an Asciidoc attribute is used.
255e5c31af7Sopenharmony_ci                write('.{externsynctitle}', file=fp)
256e5c31af7Sopenharmony_ci                write('****', file=fp)
257e5c31af7Sopenharmony_ci                write(threadsafety, file=fp, end='')
258e5c31af7Sopenharmony_ci                write('****', file=fp)
259e5c31af7Sopenharmony_ci                write('', file=fp)
260e5c31af7Sopenharmony_ci
261e5c31af7Sopenharmony_ci            # Command Properties - contained within a block, to avoid table numbering
262e5c31af7Sopenharmony_ci            if commandpropertiesentry:
263e5c31af7Sopenharmony_ci                write('.Command Properties', file=fp)
264e5c31af7Sopenharmony_ci                write('****', file=fp)
265e5c31af7Sopenharmony_ci                write('[options="header", width="100%"]', file=fp)
266e5c31af7Sopenharmony_ci                write('|====', file=fp)
267e5c31af7Sopenharmony_ci                write(self.makeCommandPropertiesTableHeader(), file=fp)
268e5c31af7Sopenharmony_ci                write(commandpropertiesentry, file=fp)
269e5c31af7Sopenharmony_ci                write('|====', file=fp)
270e5c31af7Sopenharmony_ci                write('****', file=fp)
271e5c31af7Sopenharmony_ci                write('', file=fp)
272e5c31af7Sopenharmony_ci
273e5c31af7Sopenharmony_ci            # Success Codes - contained within a block, to avoid table numbering
274e5c31af7Sopenharmony_ci            if successcodes or errorcodes:
275e5c31af7Sopenharmony_ci                write('.Return Codes', file=fp)
276e5c31af7Sopenharmony_ci                write('****', file=fp)
277e5c31af7Sopenharmony_ci                if successcodes:
278e5c31af7Sopenharmony_ci                    write('ifndef::doctype-manpage[]', file=fp)
279e5c31af7Sopenharmony_ci                    write('<<fundamentals-successcodes,Success>>::', file=fp)
280e5c31af7Sopenharmony_ci                    write('endif::doctype-manpage[]', file=fp)
281e5c31af7Sopenharmony_ci                    write('ifdef::doctype-manpage[]', file=fp)
282e5c31af7Sopenharmony_ci                    write('On success, this command returns::', file=fp)
283e5c31af7Sopenharmony_ci                    write('endif::doctype-manpage[]', file=fp)
284e5c31af7Sopenharmony_ci                    write(successcodes, file=fp)
285e5c31af7Sopenharmony_ci                if errorcodes:
286e5c31af7Sopenharmony_ci                    write('ifndef::doctype-manpage[]', file=fp)
287e5c31af7Sopenharmony_ci                    write('<<fundamentals-errorcodes,Failure>>::', file=fp)
288e5c31af7Sopenharmony_ci                    write('endif::doctype-manpage[]', file=fp)
289e5c31af7Sopenharmony_ci                    write('ifdef::doctype-manpage[]', file=fp)
290e5c31af7Sopenharmony_ci                    write('On failure, this command returns::', file=fp)
291e5c31af7Sopenharmony_ci                    write('endif::doctype-manpage[]', file=fp)
292e5c31af7Sopenharmony_ci                    write(errorcodes, file=fp)
293e5c31af7Sopenharmony_ci                write('****', file=fp)
294e5c31af7Sopenharmony_ci                write('', file=fp)
295e5c31af7Sopenharmony_ci
296e5c31af7Sopenharmony_ci    def paramIsStaticArray(self, param):
297e5c31af7Sopenharmony_ci        """Check if the parameter passed in is a static array."""
298e5c31af7Sopenharmony_ci        tail = param.find('name').tail
299e5c31af7Sopenharmony_ci        return tail and tail[0] == '['
300e5c31af7Sopenharmony_ci
301e5c31af7Sopenharmony_ci    def paramIsConst(self, param):
302e5c31af7Sopenharmony_ci        """Check if the parameter passed in has a type that mentions const."""
303e5c31af7Sopenharmony_ci        return param.text is not None and 'const' in param.text
304e5c31af7Sopenharmony_ci
305e5c31af7Sopenharmony_ci    def staticArrayLength(self, param):
306e5c31af7Sopenharmony_ci        """Get the length of a parameter that has been identified as a static array."""
307e5c31af7Sopenharmony_ci        paramenumsize = param.find('enum')
308e5c31af7Sopenharmony_ci        if paramenumsize is not None:
309e5c31af7Sopenharmony_ci            return paramenumsize.text
310e5c31af7Sopenharmony_ci            # TODO switch to below when cosmetic changes OK
311e5c31af7Sopenharmony_ci            # return self.makeEnumerantName(paramenumsize.text)
312e5c31af7Sopenharmony_ci
313e5c31af7Sopenharmony_ci        return param.find('name').tail[1:-1]
314e5c31af7Sopenharmony_ci
315e5c31af7Sopenharmony_ci    def getHandleDispatchableAncestors(self, typename):
316e5c31af7Sopenharmony_ci        """Get the ancestors of a handle object."""
317e5c31af7Sopenharmony_ci        ancestors = []
318e5c31af7Sopenharmony_ci        current = typename
319e5c31af7Sopenharmony_ci        while True:
320e5c31af7Sopenharmony_ci            current = self.getHandleParent(current)
321e5c31af7Sopenharmony_ci            if current is None:
322e5c31af7Sopenharmony_ci                return ancestors
323e5c31af7Sopenharmony_ci            if self.isHandleTypeDispatchable(current):
324e5c31af7Sopenharmony_ci                ancestors.append(current)
325e5c31af7Sopenharmony_ci
326e5c31af7Sopenharmony_ci    def isHandleTypeDispatchable(self, handlename):
327e5c31af7Sopenharmony_ci        """Check if a parent object is dispatchable or not."""
328e5c31af7Sopenharmony_ci        handle = self.registry.tree.find(
329e5c31af7Sopenharmony_ci            "types/type/[name='" + handlename + "'][@category='handle']")
330e5c31af7Sopenharmony_ci        if handle is not None and getElemType(handle) == 'VK_DEFINE_HANDLE':
331e5c31af7Sopenharmony_ci            return True
332e5c31af7Sopenharmony_ci        else:
333e5c31af7Sopenharmony_ci            return False
334e5c31af7Sopenharmony_ci
335e5c31af7Sopenharmony_ci    def isHandleOptional(self, param, params):
336e5c31af7Sopenharmony_ci        # Simple, if it is optional, return true
337e5c31af7Sopenharmony_ci        if param.get('optional') is not None:
338e5c31af7Sopenharmony_ci            return True
339e5c31af7Sopenharmony_ci
340e5c31af7Sopenharmony_ci        # If no validity is being generated, it usually means that validity is complex and not absolute, so say yes.
341e5c31af7Sopenharmony_ci        if param.get('noautovalidity') is not None:
342e5c31af7Sopenharmony_ci            return True
343e5c31af7Sopenharmony_ci
344e5c31af7Sopenharmony_ci        # If the parameter is an array and we have not already returned, find out if any of the len parameters are optional
345e5c31af7Sopenharmony_ci        if self.paramIsArray(param):
346e5c31af7Sopenharmony_ci            for length in LengthEntry.parse_len_from_param(param):
347e5c31af7Sopenharmony_ci                if not length.other_param_name:
348e5c31af7Sopenharmony_ci                    # do not care about constants or "null-terminated"
349e5c31af7Sopenharmony_ci                    continue
350e5c31af7Sopenharmony_ci
351e5c31af7Sopenharmony_ci                other_param = findNamedElem(params, length.other_param_name)
352e5c31af7Sopenharmony_ci                if other_param is None:
353e5c31af7Sopenharmony_ci                    self.logMsg('warn', length.other_param_name,
354e5c31af7Sopenharmony_ci                                'is listed as a length for parameter', param, 'but no such parameter exists')
355e5c31af7Sopenharmony_ci                if other_param and other_param.get('optional'):
356e5c31af7Sopenharmony_ci                    return True
357e5c31af7Sopenharmony_ci
358e5c31af7Sopenharmony_ci        return False
359e5c31af7Sopenharmony_ci
360e5c31af7Sopenharmony_ci    def makeOptionalPre(self, param):
361e5c31af7Sopenharmony_ci        # Do not generate this stub for bitflags
362e5c31af7Sopenharmony_ci        param_name = getElemName(param)
363e5c31af7Sopenharmony_ci        paramtype = getElemType(param)
364e5c31af7Sopenharmony_ci        type_category = self.getTypeCategory(paramtype)
365e5c31af7Sopenharmony_ci        is_optional = param.get('optional').split(',')[0] == 'true'
366e5c31af7Sopenharmony_ci        if type_category != 'bitmask' and is_optional:
367e5c31af7Sopenharmony_ci            if self.paramIsArray(param) or self.paramIsPointer(param):
368e5c31af7Sopenharmony_ci                optional_val = self.null
369e5c31af7Sopenharmony_ci            elif type_category == 'handle':
370e5c31af7Sopenharmony_ci                if self.isHandleTypeDispatchable(paramtype):
371e5c31af7Sopenharmony_ci                    optional_val = self.null
372e5c31af7Sopenharmony_ci                else:
373e5c31af7Sopenharmony_ci                    optional_val = 'dlink:' + self.conventions.api_prefix + 'NULL_HANDLE'
374e5c31af7Sopenharmony_ci            else:
375e5c31af7Sopenharmony_ci                optional_val = self.conventions.zero
376e5c31af7Sopenharmony_ci            return 'If {} is not {}, '.format(
377e5c31af7Sopenharmony_ci                self.makeParameterName(param_name),
378e5c31af7Sopenharmony_ci                optional_val)
379e5c31af7Sopenharmony_ci
380e5c31af7Sopenharmony_ci        return ""
381e5c31af7Sopenharmony_ci
382e5c31af7Sopenharmony_ci    def makeParamValidityPre(self, param, params, selector):
383e5c31af7Sopenharmony_ci        """Make the start of an entry for a parameter's validity, including a chunk of text if it is an array."""
384e5c31af7Sopenharmony_ci        param_name = getElemName(param)
385e5c31af7Sopenharmony_ci        paramtype = getElemType(param)
386e5c31af7Sopenharmony_ci
387e5c31af7Sopenharmony_ci        # General pre-amble. Check optionality and add stuff.
388e5c31af7Sopenharmony_ci        entry = ValidityEntry(anchor=(param_name, 'parameter'))
389e5c31af7Sopenharmony_ci        is_optional = param.get('optional') is not None and param.get('optional').split(',')[0] == 'true'
390e5c31af7Sopenharmony_ci
391e5c31af7Sopenharmony_ci        # This is for a union member, and the valid member is chosen by an enum selection
392e5c31af7Sopenharmony_ci        if selector:
393e5c31af7Sopenharmony_ci            selection = param.get('selection')
394e5c31af7Sopenharmony_ci
395e5c31af7Sopenharmony_ci            entry += 'If {} is {}, '.format(
396e5c31af7Sopenharmony_ci                self.makeParameterName(selector),
397e5c31af7Sopenharmony_ci                self.makeEnumerantName(selection))
398e5c31af7Sopenharmony_ci
399e5c31af7Sopenharmony_ci            if is_optional:
400e5c31af7Sopenharmony_ci                entry += "and "
401e5c31af7Sopenharmony_ci                optionalpre = self.makeOptionalPre(param)
402e5c31af7Sopenharmony_ci                entry += optionalpre[0].lower() + optionalpre[1:]
403e5c31af7Sopenharmony_ci
404e5c31af7Sopenharmony_ci            return entry
405e5c31af7Sopenharmony_ci
406e5c31af7Sopenharmony_ci        if self.paramIsStaticArray(param):
407e5c31af7Sopenharmony_ci            if paramtype != 'char':
408e5c31af7Sopenharmony_ci                entry += 'Each element of '
409e5c31af7Sopenharmony_ci            return entry
410e5c31af7Sopenharmony_ci
411e5c31af7Sopenharmony_ci        if self.paramIsArray(param) and param.get('len') != LengthEntry.NULL_TERMINATED_STRING:
412e5c31af7Sopenharmony_ci            # Find all the parameters that are called out as optional,
413e5c31af7Sopenharmony_ci            # so we can document that they might be zero, and the array may be ignored
414e5c31af7Sopenharmony_ci            optionallengths = []
415e5c31af7Sopenharmony_ci            for length in LengthEntry.parse_len_from_param(param):
416e5c31af7Sopenharmony_ci                if not length.other_param_name:
417e5c31af7Sopenharmony_ci                    # Only care about length entries that are parameter names
418e5c31af7Sopenharmony_ci                    continue
419e5c31af7Sopenharmony_ci
420e5c31af7Sopenharmony_ci                other_param = findNamedElem(params, length.other_param_name)
421e5c31af7Sopenharmony_ci                other_param_optional = (other_param is not None) and (
422e5c31af7Sopenharmony_ci                    other_param.get('optional') is not None)
423e5c31af7Sopenharmony_ci
424e5c31af7Sopenharmony_ci                if other_param is None or not other_param_optional:
425e5c31af7Sopenharmony_ci                    # Do not care about not-found params or non-optional params
426e5c31af7Sopenharmony_ci                    continue
427e5c31af7Sopenharmony_ci
428e5c31af7Sopenharmony_ci                if self.paramIsPointer(other_param):
429e5c31af7Sopenharmony_ci                    optionallengths.append(
430e5c31af7Sopenharmony_ci                        'the value referenced by ' + self.makeParameterName(length.other_param_name))
431e5c31af7Sopenharmony_ci                else:
432e5c31af7Sopenharmony_ci                    optionallengths.append(
433e5c31af7Sopenharmony_ci                        self.makeParameterName(length.other_param_name))
434e5c31af7Sopenharmony_ci
435e5c31af7Sopenharmony_ci            # Document that these arrays may be ignored if any of the length values are 0
436e5c31af7Sopenharmony_ci            if optionallengths or is_optional:
437e5c31af7Sopenharmony_ci                entry += 'If '
438e5c31af7Sopenharmony_ci            if optionallengths:
439e5c31af7Sopenharmony_ci                entry += self.makeProseListIs(optionallengths, fmt=plf.OR)
440e5c31af7Sopenharmony_ci                entry += ' not %s, ' % self.conventions.zero
441e5c31af7Sopenharmony_ci            # TODO enabling this in OpenXR, as used in Vulkan, causes nonsensical things like
442e5c31af7Sopenharmony_ci            # "If pname:propertyCapacityInput is not `0`, and pname:properties is not `NULL`, pname:properties must: be a pointer to an array of pname:propertyCapacityInput slink:XrApiLayerProperties structures"
443e5c31af7Sopenharmony_ci            if optionallengths and is_optional:
444e5c31af7Sopenharmony_ci                entry += 'and '
445e5c31af7Sopenharmony_ci            if is_optional:
446e5c31af7Sopenharmony_ci                entry += self.makeParameterName(param_name)
447e5c31af7Sopenharmony_ci                # TODO switch when cosmetic changes OK
448e5c31af7Sopenharmony_ci                # entry += ' is not {}, '.format(self.null)
449e5c31af7Sopenharmony_ci                entry += ' is not `NULL`, '
450e5c31af7Sopenharmony_ci            return entry
451e5c31af7Sopenharmony_ci
452e5c31af7Sopenharmony_ci        if param.get('optional'):
453e5c31af7Sopenharmony_ci            entry += self.makeOptionalPre(param)
454e5c31af7Sopenharmony_ci            return entry
455e5c31af7Sopenharmony_ci
456e5c31af7Sopenharmony_ci        # If none of the early returns happened, we at least return an empty
457e5c31af7Sopenharmony_ci        # entry with an anchor.
458e5c31af7Sopenharmony_ci        return entry
459e5c31af7Sopenharmony_ci
460e5c31af7Sopenharmony_ci    def createValidationLineForParameterImpl(self, blockname, param, params, typetext, selector, parentname):
461e5c31af7Sopenharmony_ci        """Make the generic validity portion used for all parameters.
462e5c31af7Sopenharmony_ci
463e5c31af7Sopenharmony_ci        May return None if nothing to validate.
464e5c31af7Sopenharmony_ci        """
465e5c31af7Sopenharmony_ci        if param.get('noautovalidity') is not None:
466e5c31af7Sopenharmony_ci            return None
467e5c31af7Sopenharmony_ci
468e5c31af7Sopenharmony_ci        validity = self.makeValidityCollection(blockname)
469e5c31af7Sopenharmony_ci        param_name = getElemName(param)
470e5c31af7Sopenharmony_ci        paramtype = getElemType(param)
471e5c31af7Sopenharmony_ci
472e5c31af7Sopenharmony_ci        entry = self.makeParamValidityPre(param, params, selector)
473e5c31af7Sopenharmony_ci
474e5c31af7Sopenharmony_ci        # pAllocator is not supported in VulkanSC and must always be NULL
475e5c31af7Sopenharmony_ci        if self.conventions.xml_api_name == "vulkansc" and param_name == 'pAllocator' and paramtype == 'VkAllocationCallbacks':
476e5c31af7Sopenharmony_ci            entry = ValidityEntry(anchor=(param_name, 'null'))
477e5c31af7Sopenharmony_ci            entry += 'pname:pAllocator must: be `NULL`'
478e5c31af7Sopenharmony_ci            return entry
479e5c31af7Sopenharmony_ci
480e5c31af7Sopenharmony_ci        # This is for a child member of a union
481e5c31af7Sopenharmony_ci        if selector:
482e5c31af7Sopenharmony_ci            entry += 'the {} member of {} must: be '.format(self.makeParameterName(param_name), self.makeParameterName(parentname))
483e5c31af7Sopenharmony_ci        else:
484e5c31af7Sopenharmony_ci            entry += '{} must: be '.format(self.makeParameterName(param_name))
485e5c31af7Sopenharmony_ci
486e5c31af7Sopenharmony_ci        if self.paramIsStaticArray(param) and paramtype == 'char':
487e5c31af7Sopenharmony_ci            # TODO this is a minor hack to determine if this is a command parameter or a struct member
488e5c31af7Sopenharmony_ci            if self.paramIsConst(param) or blockname.startswith(self.conventions.type_prefix):
489e5c31af7Sopenharmony_ci                entry += 'a null-terminated UTF-8 string whose length is less than or equal to '
490e5c31af7Sopenharmony_ci                entry += self.staticArrayLength(param)
491e5c31af7Sopenharmony_ci            else:
492e5c31af7Sopenharmony_ci                # This is a command's output parameter
493e5c31af7Sopenharmony_ci                entry += 'a character array of length %s ' % self.staticArrayLength(param)
494e5c31af7Sopenharmony_ci            validity += entry
495e5c31af7Sopenharmony_ci            return validity
496e5c31af7Sopenharmony_ci
497e5c31af7Sopenharmony_ci        elif self.paramIsArray(param):
498e5c31af7Sopenharmony_ci            # Arrays. These are hard to get right, apparently
499e5c31af7Sopenharmony_ci
500e5c31af7Sopenharmony_ci            lengths = LengthEntry.parse_len_from_param(param)
501e5c31af7Sopenharmony_ci
502e5c31af7Sopenharmony_ci            for i, length in enumerate(LengthEntry.parse_len_from_param(param)):
503e5c31af7Sopenharmony_ci                if i == 0:
504e5c31af7Sopenharmony_ci                    # If the first index, make it singular.
505e5c31af7Sopenharmony_ci                    entry += 'a '
506e5c31af7Sopenharmony_ci                    array_text = 'an array'
507e5c31af7Sopenharmony_ci                    pointer_text = self.valid_pointer_text
508e5c31af7Sopenharmony_ci                else:
509e5c31af7Sopenharmony_ci                    array_text = 'arrays'
510e5c31af7Sopenharmony_ci                    pointer_text = self.valid_pointer_text + 's'
511e5c31af7Sopenharmony_ci
512e5c31af7Sopenharmony_ci                if length.null_terminated:
513e5c31af7Sopenharmony_ci                    # This should always be the last thing.
514e5c31af7Sopenharmony_ci                    # If it ever is not for some bizarre reason, then this
515e5c31af7Sopenharmony_ci                    # will need some massaging.
516e5c31af7Sopenharmony_ci                    entry += 'null-terminated '
517e5c31af7Sopenharmony_ci                elif length.number == 1:
518e5c31af7Sopenharmony_ci                    entry += pointer_text
519e5c31af7Sopenharmony_ci                    entry += ' to '
520e5c31af7Sopenharmony_ci                else:
521e5c31af7Sopenharmony_ci                    entry += pointer_text
522e5c31af7Sopenharmony_ci                    entry += ' to '
523e5c31af7Sopenharmony_ci                    entry += array_text
524e5c31af7Sopenharmony_ci                    entry += ' of '
525e5c31af7Sopenharmony_ci                    # Handle equations, which are currently denoted with latex
526e5c31af7Sopenharmony_ci                    if length.math:
527e5c31af7Sopenharmony_ci                        # Handle equations, which are currently denoted with latex
528e5c31af7Sopenharmony_ci                        entry += str(length)
529e5c31af7Sopenharmony_ci                    else:
530e5c31af7Sopenharmony_ci                        entry += self.makeParameterName(str(length))
531e5c31af7Sopenharmony_ci                    entry += ' '
532e5c31af7Sopenharmony_ci
533e5c31af7Sopenharmony_ci            # Void pointers do not actually point at anything - remove the word "to"
534e5c31af7Sopenharmony_ci            if paramtype == 'void':
535e5c31af7Sopenharmony_ci                if lengths[-1].number == 1:
536e5c31af7Sopenharmony_ci                    if len(lengths) > 1:
537e5c31af7Sopenharmony_ci                        # Take care of the extra s added by the post array chunk function. #HACK#
538e5c31af7Sopenharmony_ci                        entry.drop_end(5)
539e5c31af7Sopenharmony_ci                    else:
540e5c31af7Sopenharmony_ci                        entry.drop_end(4)
541e5c31af7Sopenharmony_ci
542e5c31af7Sopenharmony_ci                    # This has not been hit, so this has not been tested recently.
543e5c31af7Sopenharmony_ci                    raise UnhandledCaseError(
544e5c31af7Sopenharmony_ci                        "Got void pointer param/member with last length 1")
545e5c31af7Sopenharmony_ci                else:
546e5c31af7Sopenharmony_ci                    # An array of void values is a byte array.
547e5c31af7Sopenharmony_ci                    entry += 'byte'
548e5c31af7Sopenharmony_ci
549e5c31af7Sopenharmony_ci            elif paramtype == 'char':
550e5c31af7Sopenharmony_ci                # A null terminated array of chars is a string
551e5c31af7Sopenharmony_ci                if lengths[-1].null_terminated:
552e5c31af7Sopenharmony_ci                    entry += 'UTF-8 string'
553e5c31af7Sopenharmony_ci                else:
554e5c31af7Sopenharmony_ci                    # Else it is just a bunch of chars
555e5c31af7Sopenharmony_ci                    entry += 'char value'
556e5c31af7Sopenharmony_ci
557e5c31af7Sopenharmony_ci            elif self.paramIsConst(param):
558e5c31af7Sopenharmony_ci                # If a value is "const" that means it will not get modified,
559e5c31af7Sopenharmony_ci                # so it must be valid going into the function.
560e5c31af7Sopenharmony_ci                if 'const' in param.text:
561e5c31af7Sopenharmony_ci
562e5c31af7Sopenharmony_ci                    if not self.isStructAlwaysValid(paramtype):
563e5c31af7Sopenharmony_ci                        entry += 'valid '
564e5c31af7Sopenharmony_ci
565e5c31af7Sopenharmony_ci            # Check if the array elements are optional
566e5c31af7Sopenharmony_ci            array_element_optional = param.get('optional') is not None    \
567e5c31af7Sopenharmony_ci                      and len(param.get('optional').split(',')) == len(LengthEntry.parse_len_from_param(param)) + 1 \
568e5c31af7Sopenharmony_ci                      and param.get('optional').split(',')[-1] == 'true'
569e5c31af7Sopenharmony_ci            if array_element_optional and self.getTypeCategory(paramtype) != 'bitmask': # bitmask is handled later
570e5c31af7Sopenharmony_ci                entry += 'or dlink:' + self.conventions.api_prefix + 'NULL_HANDLE '
571e5c31af7Sopenharmony_ci
572e5c31af7Sopenharmony_ci            entry += typetext
573e5c31af7Sopenharmony_ci
574e5c31af7Sopenharmony_ci            # pluralize
575e5c31af7Sopenharmony_ci            if len(lengths) > 1 or (lengths[0] != 1 and not lengths[0].null_terminated):
576e5c31af7Sopenharmony_ci                entry += 's'
577e5c31af7Sopenharmony_ci
578e5c31af7Sopenharmony_ci            return self.handleRequiredBitmask(blockname, param, paramtype, entry, 'true' if array_element_optional else None)
579e5c31af7Sopenharmony_ci
580e5c31af7Sopenharmony_ci        if self.paramIsPointer(param):
581e5c31af7Sopenharmony_ci            # Handle pointers - which are really special case arrays (i.e.
582e5c31af7Sopenharmony_ci            # they do not have a length)
583e5c31af7Sopenharmony_ci            # TODO  should do something here if someone ever uses some intricate comma-separated `optional`
584e5c31af7Sopenharmony_ci            pointercount = param.find('type').tail.count('*')
585e5c31af7Sopenharmony_ci
586e5c31af7Sopenharmony_ci            # Treat void* as an int
587e5c31af7Sopenharmony_ci            if paramtype == 'void':
588e5c31af7Sopenharmony_ci                optional = param.get('optional')
589e5c31af7Sopenharmony_ci                # If there is only void*, it is just optional int - we do not need any language.
590e5c31af7Sopenharmony_ci                if pointercount == 1 and optional is not None:
591e5c31af7Sopenharmony_ci                    return None  # early return
592e5c31af7Sopenharmony_ci                # Treat the inner-most void* as an int
593e5c31af7Sopenharmony_ci                pointercount -= 1
594e5c31af7Sopenharmony_ci
595e5c31af7Sopenharmony_ci            # Could be multi-level pointers (e.g. ppData - pointer to a pointer). Handle that.
596e5c31af7Sopenharmony_ci            entry += 'a '
597e5c31af7Sopenharmony_ci            entry += (self.valid_pointer_text + ' to a ') * pointercount
598e5c31af7Sopenharmony_ci
599e5c31af7Sopenharmony_ci            # Handle void* and pointers to it
600e5c31af7Sopenharmony_ci            if paramtype == 'void':
601e5c31af7Sopenharmony_ci                if optional is None or optional.split(',')[pointercount]:
602e5c31af7Sopenharmony_ci                    # The last void* is just optional int (e.g. to be filled by the impl.)
603e5c31af7Sopenharmony_ci                    typetext = 'pointer value'
604e5c31af7Sopenharmony_ci
605e5c31af7Sopenharmony_ci            # If a value is "const" that means it will not get modified, so
606e5c31af7Sopenharmony_ci            # it must be valid going into the function.
607e5c31af7Sopenharmony_ci            elif self.paramIsConst(param) and paramtype != 'void':
608e5c31af7Sopenharmony_ci                entry += 'valid '
609e5c31af7Sopenharmony_ci
610e5c31af7Sopenharmony_ci            entry += typetext
611e5c31af7Sopenharmony_ci            return self.handleRequiredBitmask(blockname, param, paramtype, entry, param.get('optional'))
612e5c31af7Sopenharmony_ci
613e5c31af7Sopenharmony_ci        # Add additional line for non-optional bitmasks
614e5c31af7Sopenharmony_ci        if self.getTypeCategory(paramtype) == 'bitmask':
615e5c31af7Sopenharmony_ci            # TODO does not really handle if someone tries something like optional="true,false"
616e5c31af7Sopenharmony_ci            # TODO OpenXR has 0 or a valid combination of flags, for optional things.
617e5c31af7Sopenharmony_ci            # Vulkan does not...
618e5c31af7Sopenharmony_ci            # isMandatory = param.get('optional') is None
619e5c31af7Sopenharmony_ci            # if not isMandatory:
620e5c31af7Sopenharmony_ci            #     entry += self.conventions.zero
621e5c31af7Sopenharmony_ci            #     entry += ' or '
622e5c31af7Sopenharmony_ci            # Non-pointer, non-optional things must be valid
623e5c31af7Sopenharmony_ci            entry += 'a valid {}'.format(typetext)
624e5c31af7Sopenharmony_ci
625e5c31af7Sopenharmony_ci            return self.handleRequiredBitmask(blockname, param, paramtype, entry, param.get('optional'))
626e5c31af7Sopenharmony_ci
627e5c31af7Sopenharmony_ci        # Non-pointer, non-optional things must be valid
628e5c31af7Sopenharmony_ci        entry += 'a valid {}'.format(typetext)
629e5c31af7Sopenharmony_ci        return entry
630e5c31af7Sopenharmony_ci
631e5c31af7Sopenharmony_ci    def handleRequiredBitmask(self, blockname, param, paramtype, entry, optional):
632e5c31af7Sopenharmony_ci        # TODO does not really handle if someone tries something like optional="true,false"
633e5c31af7Sopenharmony_ci        if self.getTypeCategory(paramtype) != 'bitmask' or optional == 'true':
634e5c31af7Sopenharmony_ci            return entry
635e5c31af7Sopenharmony_ci        if self.paramIsPointer(param) and not self.paramIsArray(param):
636e5c31af7Sopenharmony_ci            # This is presumably an output parameter
637e5c31af7Sopenharmony_ci            return entry
638e5c31af7Sopenharmony_ci
639e5c31af7Sopenharmony_ci        param_name = getElemName(param)
640e5c31af7Sopenharmony_ci        # If mandatory, then we need two entries instead of just one.
641e5c31af7Sopenharmony_ci        validity = self.makeValidityCollection(blockname)
642e5c31af7Sopenharmony_ci        validity += entry
643e5c31af7Sopenharmony_ci
644e5c31af7Sopenharmony_ci        entry2 = ValidityEntry(anchor=(param_name, 'requiredbitmask'))
645e5c31af7Sopenharmony_ci        if self.paramIsArray(param):
646e5c31af7Sopenharmony_ci            entry2 += 'Each element of '
647e5c31af7Sopenharmony_ci        entry2 += '{} must: not be {}'.format(
648e5c31af7Sopenharmony_ci            self.makeParameterName(param_name), self.conventions.zero)
649e5c31af7Sopenharmony_ci        validity += entry2
650e5c31af7Sopenharmony_ci        return validity
651e5c31af7Sopenharmony_ci
652e5c31af7Sopenharmony_ci    def createValidationLineForParameter(self, blockname, param, params, typecategory, selector, parentname):
653e5c31af7Sopenharmony_ci        """Make an entire validation entry for a given parameter."""
654e5c31af7Sopenharmony_ci        param_name = getElemName(param)
655e5c31af7Sopenharmony_ci        paramtype = getElemType(param)
656e5c31af7Sopenharmony_ci
657e5c31af7Sopenharmony_ci        is_array = self.paramIsArray(param)
658e5c31af7Sopenharmony_ci        is_pointer = self.paramIsPointer(param)
659e5c31af7Sopenharmony_ci        needs_recursive_validity = (is_array
660e5c31af7Sopenharmony_ci                                    or is_pointer
661e5c31af7Sopenharmony_ci                                    or not self.isStructAlwaysValid(paramtype))
662e5c31af7Sopenharmony_ci        typetext = None
663e5c31af7Sopenharmony_ci        if paramtype in ('void', 'char'):
664e5c31af7Sopenharmony_ci            # Chars and void are special cases - we call the impl function,
665e5c31af7Sopenharmony_ci            # but do not use the typetext.
666e5c31af7Sopenharmony_ci            # A null-terminated char array is a string, else it is chars.
667e5c31af7Sopenharmony_ci            # An array of void values is a byte array, a void pointer is just a pointer to nothing in particular
668e5c31af7Sopenharmony_ci            typetext = ''
669e5c31af7Sopenharmony_ci
670e5c31af7Sopenharmony_ci        elif typecategory == 'bitmask':
671e5c31af7Sopenharmony_ci            bitsname = paramtype.replace('Flags', 'FlagBits')
672e5c31af7Sopenharmony_ci            bitselem = self.registry.tree.find("enums[@name='" + bitsname + "']")
673e5c31af7Sopenharmony_ci
674e5c31af7Sopenharmony_ci            # If bitsname is an alias, then use the alias to get bitselem.
675e5c31af7Sopenharmony_ci            typeElem = self.registry.lookupElementInfo(bitsname, self.registry.typedict)
676e5c31af7Sopenharmony_ci            if typeElem is not None:
677e5c31af7Sopenharmony_ci                alias = self.registry.getAlias(typeElem.elem, self.registry.typedict)
678e5c31af7Sopenharmony_ci                if alias is not None:
679e5c31af7Sopenharmony_ci                    bitselem = self.registry.tree.find("enums[@name='" + alias + "']")
680e5c31af7Sopenharmony_ci
681e5c31af7Sopenharmony_ci            if bitselem is None or len(bitselem.findall('enum[@required="true"]')) == 0:
682e5c31af7Sopenharmony_ci                # Empty bit mask: presumably just a placeholder (or only in
683e5c31af7Sopenharmony_ci                # an extension not enabled for this build)
684e5c31af7Sopenharmony_ci                entry = ValidityEntry(
685e5c31af7Sopenharmony_ci                    anchor=(param_name, 'zerobitmask'))
686e5c31af7Sopenharmony_ci                entry += self.makeParameterName(param_name)
687e5c31af7Sopenharmony_ci                entry += ' must: be '
688e5c31af7Sopenharmony_ci                entry += self.conventions.zero
689e5c31af7Sopenharmony_ci                # Early return
690e5c31af7Sopenharmony_ci                return entry
691e5c31af7Sopenharmony_ci
692e5c31af7Sopenharmony_ci            is_const = self.paramIsConst(param)
693e5c31af7Sopenharmony_ci
694e5c31af7Sopenharmony_ci            if is_array:
695e5c31af7Sopenharmony_ci                if is_const:
696e5c31af7Sopenharmony_ci                    # input an array of bitmask values
697e5c31af7Sopenharmony_ci                    template = 'combinations of {bitsname} value'
698e5c31af7Sopenharmony_ci                else:
699e5c31af7Sopenharmony_ci                    template = '{paramtype} value'
700e5c31af7Sopenharmony_ci            elif is_pointer:
701e5c31af7Sopenharmony_ci                if is_const:
702e5c31af7Sopenharmony_ci                    template = 'combination of {bitsname} values'
703e5c31af7Sopenharmony_ci                else:
704e5c31af7Sopenharmony_ci                    template = '{paramtype} value'
705e5c31af7Sopenharmony_ci            else:
706e5c31af7Sopenharmony_ci                template = 'combination of {bitsname} values'
707e5c31af7Sopenharmony_ci
708e5c31af7Sopenharmony_ci            # The above few cases all use makeEnumerationName, just with different context.
709e5c31af7Sopenharmony_ci            typetext = template.format(
710e5c31af7Sopenharmony_ci                bitsname=self.makeEnumerationName(bitsname),
711e5c31af7Sopenharmony_ci                paramtype=self.makeFlagsName(paramtype))
712e5c31af7Sopenharmony_ci
713e5c31af7Sopenharmony_ci        elif typecategory == 'handle':
714e5c31af7Sopenharmony_ci            typetext = '{} handle'.format(self.makeStructName(paramtype))
715e5c31af7Sopenharmony_ci
716e5c31af7Sopenharmony_ci        elif typecategory == 'enum':
717e5c31af7Sopenharmony_ci            typetext = '{} value'.format(self.makeEnumerationName(paramtype))
718e5c31af7Sopenharmony_ci
719e5c31af7Sopenharmony_ci        elif typecategory == 'funcpointer':
720e5c31af7Sopenharmony_ci            typetext = '{} value'.format(self.makeFuncPointerName(paramtype))
721e5c31af7Sopenharmony_ci
722e5c31af7Sopenharmony_ci        elif typecategory == 'struct':
723e5c31af7Sopenharmony_ci            if needs_recursive_validity:
724e5c31af7Sopenharmony_ci                typetext = '{} structure'.format(
725e5c31af7Sopenharmony_ci                    self.makeStructName(paramtype))
726e5c31af7Sopenharmony_ci
727e5c31af7Sopenharmony_ci        elif typecategory == 'union':
728e5c31af7Sopenharmony_ci            if needs_recursive_validity:
729e5c31af7Sopenharmony_ci                typetext = '{} union'.format(self.makeStructName(paramtype))
730e5c31af7Sopenharmony_ci
731e5c31af7Sopenharmony_ci        elif self.paramIsArray(param) or self.paramIsPointer(param):
732e5c31af7Sopenharmony_ci            # TODO sync cosmetic changes from OpenXR?
733e5c31af7Sopenharmony_ci            if typecategory is None:
734e5c31af7Sopenharmony_ci                typetext = f'code:{paramtype} value'
735e5c31af7Sopenharmony_ci            else:
736e5c31af7Sopenharmony_ci                typetext = '{} value'.format(self.makeBaseTypeName(paramtype))
737e5c31af7Sopenharmony_ci
738e5c31af7Sopenharmony_ci        elif typecategory is None:
739e5c31af7Sopenharmony_ci            if not self.isStructAlwaysValid(paramtype):
740e5c31af7Sopenharmony_ci                typetext = '{} value'.format(
741e5c31af7Sopenharmony_ci                    self.makeExternalTypeName(paramtype))
742e5c31af7Sopenharmony_ci
743e5c31af7Sopenharmony_ci            # "a valid uint32_t value" does not make much sense.
744e5c31af7Sopenharmony_ci            pass
745e5c31af7Sopenharmony_ci
746e5c31af7Sopenharmony_ci        # If any of the above conditions matched and set typetext,
747e5c31af7Sopenharmony_ci        # we call using it.
748e5c31af7Sopenharmony_ci        if typetext is not None:
749e5c31af7Sopenharmony_ci            return self.createValidationLineForParameterImpl(
750e5c31af7Sopenharmony_ci                blockname, param, params, typetext, selector, parentname)
751e5c31af7Sopenharmony_ci        return None
752e5c31af7Sopenharmony_ci
753e5c31af7Sopenharmony_ci    def makeHandleValidityParent(self, param, params):
754e5c31af7Sopenharmony_ci        """Make a validity entry for a handle's parent object.
755e5c31af7Sopenharmony_ci
756e5c31af7Sopenharmony_ci        Creates 'parent' VUID.
757e5c31af7Sopenharmony_ci        """
758e5c31af7Sopenharmony_ci        param_name = getElemName(param)
759e5c31af7Sopenharmony_ci        paramtype = getElemType(param)
760e5c31af7Sopenharmony_ci
761e5c31af7Sopenharmony_ci        # Iterate up the handle parent hierarchy for the first parameter of
762e5c31af7Sopenharmony_ci        # a parent type.
763e5c31af7Sopenharmony_ci        # This enables cases where a more distant ancestor is present, such
764e5c31af7Sopenharmony_ci        # as VkDevice and VkCommandBuffer (but no direct parent
765e5c31af7Sopenharmony_ci        # VkCommandPool).
766e5c31af7Sopenharmony_ci
767e5c31af7Sopenharmony_ci        while True:
768e5c31af7Sopenharmony_ci            # If we run out of ancestors, give up
769e5c31af7Sopenharmony_ci            handleparent = self.getHandleParent(paramtype)
770e5c31af7Sopenharmony_ci            if handleparent is None:
771e5c31af7Sopenharmony_ci                if self.trace:
772e5c31af7Sopenharmony_ci                    print(f'makeHandleValidityParent:{param_name} has no handle parent, skipping')
773e5c31af7Sopenharmony_ci                return None
774e5c31af7Sopenharmony_ci
775e5c31af7Sopenharmony_ci            # Look for a parameter of the ancestor type
776e5c31af7Sopenharmony_ci            otherparam = findTypedElem(params, handleparent)
777e5c31af7Sopenharmony_ci            if otherparam is not None:
778e5c31af7Sopenharmony_ci                break
779e5c31af7Sopenharmony_ci
780e5c31af7Sopenharmony_ci            # Continue up the hierarchy
781e5c31af7Sopenharmony_ci            paramtype = handleparent
782e5c31af7Sopenharmony_ci
783e5c31af7Sopenharmony_ci        parent_name = getElemName(otherparam)
784e5c31af7Sopenharmony_ci        entry = ValidityEntry(anchor=(param_name, 'parent'))
785e5c31af7Sopenharmony_ci
786e5c31af7Sopenharmony_ci        is_optional = self.isHandleOptional(param, params)
787e5c31af7Sopenharmony_ci
788e5c31af7Sopenharmony_ci        if self.paramIsArray(param):
789e5c31af7Sopenharmony_ci            template = 'Each element of {}'
790e5c31af7Sopenharmony_ci            if is_optional:
791e5c31af7Sopenharmony_ci                template += ' that is a valid handle'
792e5c31af7Sopenharmony_ci        elif is_optional:
793e5c31af7Sopenharmony_ci            template = 'If {} is a valid handle, it'
794e5c31af7Sopenharmony_ci        else:
795e5c31af7Sopenharmony_ci            # not optional, not an array. Just say the parameter name.
796e5c31af7Sopenharmony_ci            template = '{}'
797e5c31af7Sopenharmony_ci
798e5c31af7Sopenharmony_ci        entry += template.format(self.makeParameterName(param_name))
799e5c31af7Sopenharmony_ci
800e5c31af7Sopenharmony_ci        entry += ' must: have been created, allocated, or retrieved from {}'.format(
801e5c31af7Sopenharmony_ci            self.makeParameterName(parent_name))
802e5c31af7Sopenharmony_ci
803e5c31af7Sopenharmony_ci        return entry
804e5c31af7Sopenharmony_ci
805e5c31af7Sopenharmony_ci    def makeAsciiDocHandlesCommonAncestor(self, blockname, handles, params):
806e5c31af7Sopenharmony_ci        """Make an asciidoc validity entry for a common ancestors between handles.
807e5c31af7Sopenharmony_ci
808e5c31af7Sopenharmony_ci        Only handles parent validity for signatures taking multiple handles
809e5c31af7Sopenharmony_ci        any ancestors also being supplied to this function.
810e5c31af7Sopenharmony_ci        (e.g. "Each of x, y, and z must: come from the same slink:ParentHandle")
811e5c31af7Sopenharmony_ci        See self.makeAsciiDocHandleParent() for instances where the parent
812e5c31af7Sopenharmony_ci        handle is named and also passed.
813e5c31af7Sopenharmony_ci
814e5c31af7Sopenharmony_ci        Creates 'commonparent' VUID.
815e5c31af7Sopenharmony_ci        """
816e5c31af7Sopenharmony_ci        # TODO Replace with refactored code from OpenXR
817e5c31af7Sopenharmony_ci        entry = None
818e5c31af7Sopenharmony_ci
819e5c31af7Sopenharmony_ci        if len(handles) > 1:
820e5c31af7Sopenharmony_ci            ancestormap = {}
821e5c31af7Sopenharmony_ci            anyoptional = False
822e5c31af7Sopenharmony_ci            # Find all the ancestors
823e5c31af7Sopenharmony_ci            for param in handles:
824e5c31af7Sopenharmony_ci                paramtype = getElemType(param)
825e5c31af7Sopenharmony_ci
826e5c31af7Sopenharmony_ci                if not self.paramIsPointer(param) or (param.text and 'const' in param.text):
827e5c31af7Sopenharmony_ci                    ancestors = self.getHandleDispatchableAncestors(paramtype)
828e5c31af7Sopenharmony_ci
829e5c31af7Sopenharmony_ci                    ancestormap[param] = ancestors
830e5c31af7Sopenharmony_ci
831e5c31af7Sopenharmony_ci                    anyoptional |= self.isHandleOptional(param, params)
832e5c31af7Sopenharmony_ci
833e5c31af7Sopenharmony_ci            # Remove redundant ancestor lists
834e5c31af7Sopenharmony_ci            for param in handles:
835e5c31af7Sopenharmony_ci                paramtype = getElemType(param)
836e5c31af7Sopenharmony_ci
837e5c31af7Sopenharmony_ci                removals = []
838e5c31af7Sopenharmony_ci                for ancestors in ancestormap.items():
839e5c31af7Sopenharmony_ci                    if paramtype in ancestors[1]:
840e5c31af7Sopenharmony_ci                        removals.append(ancestors[0])
841e5c31af7Sopenharmony_ci
842e5c31af7Sopenharmony_ci                if removals != []:
843e5c31af7Sopenharmony_ci                    for removal in removals:
844e5c31af7Sopenharmony_ci                        del(ancestormap[removal])
845e5c31af7Sopenharmony_ci
846e5c31af7Sopenharmony_ci            # Intersect
847e5c31af7Sopenharmony_ci
848e5c31af7Sopenharmony_ci            if len(ancestormap.values()) > 1:
849e5c31af7Sopenharmony_ci                current = list(ancestormap.values())[0]
850e5c31af7Sopenharmony_ci                for ancestors in list(ancestormap.values())[1:]:
851e5c31af7Sopenharmony_ci                    current = [val for val in current if val in ancestors]
852e5c31af7Sopenharmony_ci
853e5c31af7Sopenharmony_ci                if len(current) > 0:
854e5c31af7Sopenharmony_ci                    commonancestor = current[0]
855e5c31af7Sopenharmony_ci
856e5c31af7Sopenharmony_ci                    if len(ancestormap.keys()) > 1:
857e5c31af7Sopenharmony_ci
858e5c31af7Sopenharmony_ci                        entry = ValidityEntry(anchor=('commonparent',))
859e5c31af7Sopenharmony_ci
860e5c31af7Sopenharmony_ci                        parametertexts = []
861e5c31af7Sopenharmony_ci                        for param in ancestormap.keys():
862e5c31af7Sopenharmony_ci                            param_name = getElemName(param)
863e5c31af7Sopenharmony_ci                            parametertext = self.makeParameterName(param_name)
864e5c31af7Sopenharmony_ci                            if self.paramIsArray(param):
865e5c31af7Sopenharmony_ci                                parametertext = 'the elements of ' + parametertext
866e5c31af7Sopenharmony_ci                            parametertexts.append(parametertext)
867e5c31af7Sopenharmony_ci
868e5c31af7Sopenharmony_ci                        parametertexts.sort()
869e5c31af7Sopenharmony_ci
870e5c31af7Sopenharmony_ci                        if len(parametertexts) > 2:
871e5c31af7Sopenharmony_ci                            entry += 'Each of '
872e5c31af7Sopenharmony_ci                        else:
873e5c31af7Sopenharmony_ci                            entry += 'Both of '
874e5c31af7Sopenharmony_ci
875e5c31af7Sopenharmony_ci                        entry += self.makeProseList(parametertexts,
876e5c31af7Sopenharmony_ci                                                    comma_for_two_elts=True)
877e5c31af7Sopenharmony_ci                        if anyoptional is True:
878e5c31af7Sopenharmony_ci                            entry += ' that are valid handles of non-ignored parameters'
879e5c31af7Sopenharmony_ci                        entry += ' must: have been created, allocated, or retrieved from the same '
880e5c31af7Sopenharmony_ci                        entry += self.makeStructName(commonancestor)
881e5c31af7Sopenharmony_ci
882e5c31af7Sopenharmony_ci        return entry
883e5c31af7Sopenharmony_ci
884e5c31af7Sopenharmony_ci    def makeStructureTypeFromName(self, structname):
885e5c31af7Sopenharmony_ci        """Create text for a structure type name, like ename:VK_STRUCTURE_TYPE_CREATE_INSTANCE_INFO"""
886e5c31af7Sopenharmony_ci        return self.makeEnumerantName(self.conventions.generate_structure_type_from_name(structname))
887e5c31af7Sopenharmony_ci
888e5c31af7Sopenharmony_ci    def makeStructureTypeValidity(self, structname):
889e5c31af7Sopenharmony_ci        """Generate an validity line for the type value of a struct.
890e5c31af7Sopenharmony_ci
891e5c31af7Sopenharmony_ci        Creates VUID named like the member name.
892e5c31af7Sopenharmony_ci        """
893e5c31af7Sopenharmony_ci        info = self.registry.typedict.get(structname)
894e5c31af7Sopenharmony_ci        assert(info is not None)
895e5c31af7Sopenharmony_ci
896e5c31af7Sopenharmony_ci        # If this fails (meaning we have something other than a struct in here),
897e5c31af7Sopenharmony_ci        # then the caller is wrong:
898e5c31af7Sopenharmony_ci        # probably passing the wrong value for structname.
899e5c31af7Sopenharmony_ci        members = info.getMembers()
900e5c31af7Sopenharmony_ci        assert(members)
901e5c31af7Sopenharmony_ci
902e5c31af7Sopenharmony_ci        # If this fails, see caller: this should only get called for a struct type with a type value.
903e5c31af7Sopenharmony_ci        param = findNamedElem(members, self.structtype_member_name)
904e5c31af7Sopenharmony_ci        # OpenXR gets some structs without a type field in here, so cannot assert
905e5c31af7Sopenharmony_ci        assert(param is not None)
906e5c31af7Sopenharmony_ci        # if param is None:
907e5c31af7Sopenharmony_ci        #     return None
908e5c31af7Sopenharmony_ci
909e5c31af7Sopenharmony_ci        entry = ValidityEntry(
910e5c31af7Sopenharmony_ci            anchor=(self.structtype_member_name, self.structtype_member_name))
911e5c31af7Sopenharmony_ci        entry += self.makeParameterName(self.structtype_member_name)
912e5c31af7Sopenharmony_ci        entry += ' must: be '
913e5c31af7Sopenharmony_ci
914e5c31af7Sopenharmony_ci        values = param.get('values', '').split(',')
915e5c31af7Sopenharmony_ci        if values:
916e5c31af7Sopenharmony_ci            # Extract each enumerant value. They could be validated in the
917e5c31af7Sopenharmony_ci            # same fashion as validextensionstructs in
918e5c31af7Sopenharmony_ci            # makeStructureExtensionPointer, although that is not relevant in
919e5c31af7Sopenharmony_ci            # the current extension struct model.
920e5c31af7Sopenharmony_ci            entry += self.makeProseList((self.makeEnumerantName(v)
921e5c31af7Sopenharmony_ci                                         for v in values), 'or')
922e5c31af7Sopenharmony_ci            return entry
923e5c31af7Sopenharmony_ci
924e5c31af7Sopenharmony_ci        if 'Base' in structname:
925e5c31af7Sopenharmony_ci            # This type does not even have any values for its type, and it
926e5c31af7Sopenharmony_ci            # seems like it might be a base struct that we would expect to
927e5c31af7Sopenharmony_ci            # lack its own type, so omit the entire statement
928e5c31af7Sopenharmony_ci            return None
929e5c31af7Sopenharmony_ci
930e5c31af7Sopenharmony_ci        self.logMsg('warn', 'No values were marked-up for the structure type member of',
931e5c31af7Sopenharmony_ci                    structname, 'so making one up!')
932e5c31af7Sopenharmony_ci        entry += self.makeStructureTypeFromName(structname)
933e5c31af7Sopenharmony_ci
934e5c31af7Sopenharmony_ci        return entry
935e5c31af7Sopenharmony_ci
936e5c31af7Sopenharmony_ci    def makeStructureExtensionPointer(self, blockname, param):
937e5c31af7Sopenharmony_ci        """Generate an validity line for the pointer chain member value of a struct."""
938e5c31af7Sopenharmony_ci        param_name = getElemName(param)
939e5c31af7Sopenharmony_ci
940e5c31af7Sopenharmony_ci        if param.get('validextensionstructs') is not None:
941e5c31af7Sopenharmony_ci            self.logMsg('warn', blockname,
942e5c31af7Sopenharmony_ci                        'validextensionstructs is deprecated/removed', '\n')
943e5c31af7Sopenharmony_ci
944e5c31af7Sopenharmony_ci        entry = ValidityEntry(
945e5c31af7Sopenharmony_ci            anchor=(param_name, self.nextpointer_member_name))
946e5c31af7Sopenharmony_ci        validextensionstructs = self.registry.validextensionstructs.get(
947e5c31af7Sopenharmony_ci            blockname)
948e5c31af7Sopenharmony_ci        extensionstructs = []
949e5c31af7Sopenharmony_ci        duplicatestructs = []
950e5c31af7Sopenharmony_ci
951e5c31af7Sopenharmony_ci        if validextensionstructs is not None:
952e5c31af7Sopenharmony_ci            # Check each structure name and skip it if not required by the
953e5c31af7Sopenharmony_ci            # generator. This allows tagging extension structs in the XML
954e5c31af7Sopenharmony_ci            # that are only included in validity when needed for the spec
955e5c31af7Sopenharmony_ci            # being targeted.
956e5c31af7Sopenharmony_ci            # Track the required structures, and of the required structures,
957e5c31af7Sopenharmony_ci            # those that allow duplicates in the pNext chain.
958e5c31af7Sopenharmony_ci            for struct in validextensionstructs:
959e5c31af7Sopenharmony_ci                # Unpleasantly breaks encapsulation. Should be a method in the registry class
960e5c31af7Sopenharmony_ci                t = self.registry.lookupElementInfo(
961e5c31af7Sopenharmony_ci                    struct, self.registry.typedict)
962e5c31af7Sopenharmony_ci                if t is None:
963e5c31af7Sopenharmony_ci                    self.logMsg('warn', 'makeStructureExtensionPointer: struct', struct,
964e5c31af7Sopenharmony_ci                                'is in a validextensionstructs= attribute but is not in the registry')
965e5c31af7Sopenharmony_ci                elif t.required:
966e5c31af7Sopenharmony_ci                    extensionstructs.append('slink:' + struct)
967e5c31af7Sopenharmony_ci                    if t.elem.get('allowduplicate') == 'true':
968e5c31af7Sopenharmony_ci                        duplicatestructs.append('slink:' + struct)
969e5c31af7Sopenharmony_ci                else:
970e5c31af7Sopenharmony_ci                    self.logMsg(
971e5c31af7Sopenharmony_ci                        'diag', 'makeStructureExtensionPointer: struct', struct, 'IS NOT required')
972e5c31af7Sopenharmony_ci
973e5c31af7Sopenharmony_ci        if not extensionstructs:
974e5c31af7Sopenharmony_ci            entry += '{} must: be {}'.format(
975e5c31af7Sopenharmony_ci                self.makeParameterName(param_name), self.null)
976e5c31af7Sopenharmony_ci            return entry
977e5c31af7Sopenharmony_ci
978e5c31af7Sopenharmony_ci        if len(extensionstructs) == 1:
979e5c31af7Sopenharmony_ci            entry += '{} must: be {} or a pointer to a valid instance of {}'.format(self.makeParameterName(param_name), self.null,
980e5c31af7Sopenharmony_ci                                                                                    extensionstructs[0])
981e5c31af7Sopenharmony_ci        else:
982e5c31af7Sopenharmony_ci            # More than one extension struct.
983e5c31af7Sopenharmony_ci            entry += 'Each {} member of any structure (including this one) in the pname:{} chain '.format(
984e5c31af7Sopenharmony_ci                self.makeParameterName(param_name), self.nextpointer_member_name)
985e5c31af7Sopenharmony_ci            entry += 'must: be either {} or a pointer to a valid instance of '.format(
986e5c31af7Sopenharmony_ci                self.null)
987e5c31af7Sopenharmony_ci
988e5c31af7Sopenharmony_ci            entry += self.makeProseList(extensionstructs, fmt=plf.OR)
989e5c31af7Sopenharmony_ci
990e5c31af7Sopenharmony_ci        validity = self.makeValidityCollection(blockname)
991e5c31af7Sopenharmony_ci        validity += entry
992e5c31af7Sopenharmony_ci
993e5c31af7Sopenharmony_ci        # Generate VU statement requiring unique structures in the pNext
994e5c31af7Sopenharmony_ci        # chain.
995e5c31af7Sopenharmony_ci        # NOTE: OpenXR always allows non-unique type values. Instances other
996e5c31af7Sopenharmony_ci        # than the first are just ignored
997e5c31af7Sopenharmony_ci
998e5c31af7Sopenharmony_ci        vu = ('The pname:' +
999e5c31af7Sopenharmony_ci              self.structtype_member_name +
1000e5c31af7Sopenharmony_ci              ' value of each struct in the pname:' +
1001e5c31af7Sopenharmony_ci              self.nextpointer_member_name +
1002e5c31af7Sopenharmony_ci              ' chain must: be unique')
1003e5c31af7Sopenharmony_ci        anchor = (self.conventions.member_used_for_unique_vuid, 'unique')
1004e5c31af7Sopenharmony_ci
1005e5c31af7Sopenharmony_ci        # If duplicates of some structures are allowed, they are called out
1006e5c31af7Sopenharmony_ci        # explicitly.
1007e5c31af7Sopenharmony_ci        num = len(duplicatestructs)
1008e5c31af7Sopenharmony_ci        if num > 0:
1009e5c31af7Sopenharmony_ci            vu = (vu +
1010e5c31af7Sopenharmony_ci                  ', with the exception of structures of type ' +
1011e5c31af7Sopenharmony_ci                  self.makeProseList(duplicatestructs, fmt=plf.OR))
1012e5c31af7Sopenharmony_ci
1013e5c31af7Sopenharmony_ci        validity.addValidityEntry(vu, anchor = anchor )
1014e5c31af7Sopenharmony_ci
1015e5c31af7Sopenharmony_ci        return validity
1016e5c31af7Sopenharmony_ci
1017e5c31af7Sopenharmony_ci    def addSharedStructMemberValidity(self, struct, blockname, param, validity):
1018e5c31af7Sopenharmony_ci        """Generate language to independently validate a parameter, for those validated even in output.
1019e5c31af7Sopenharmony_ci
1020e5c31af7Sopenharmony_ci        Return value indicates whether it was handled internally (True) or if it may need more validity (False)."""
1021e5c31af7Sopenharmony_ci        param_name = getElemName(param)
1022e5c31af7Sopenharmony_ci        paramtype = getElemType(param)
1023e5c31af7Sopenharmony_ci        if param.get('noautovalidity') is None:
1024e5c31af7Sopenharmony_ci
1025e5c31af7Sopenharmony_ci            if self.conventions.is_structure_type_member(paramtype, param_name):
1026e5c31af7Sopenharmony_ci                validity += self.makeStructureTypeValidity(blockname)
1027e5c31af7Sopenharmony_ci                return True
1028e5c31af7Sopenharmony_ci
1029e5c31af7Sopenharmony_ci            if self.conventions.is_nextpointer_member(paramtype, param_name):
1030e5c31af7Sopenharmony_ci                # Vulkan: the addition of validity here is conditional unlike OpenXR.
1031e5c31af7Sopenharmony_ci                if struct.get('structextends') is None:
1032e5c31af7Sopenharmony_ci                    validity += self.makeStructureExtensionPointer(
1033e5c31af7Sopenharmony_ci                        blockname, param)
1034e5c31af7Sopenharmony_ci                return True
1035e5c31af7Sopenharmony_ci        return False
1036e5c31af7Sopenharmony_ci
1037e5c31af7Sopenharmony_ci    def makeOutputOnlyStructValidity(self, cmd, blockname, params):
1038e5c31af7Sopenharmony_ci        """Generate all the valid usage information for a struct that is entirely output.
1039e5c31af7Sopenharmony_ci
1040e5c31af7Sopenharmony_ci        That is, it is only ever filled out by the implementation other than
1041e5c31af7Sopenharmony_ci        the structure type and pointer chain members.
1042e5c31af7Sopenharmony_ci        Thus, we only create validity for the pointer chain member.
1043e5c31af7Sopenharmony_ci        """
1044e5c31af7Sopenharmony_ci        # Start the validity collection for this struct
1045e5c31af7Sopenharmony_ci        validity = self.makeValidityCollection(blockname)
1046e5c31af7Sopenharmony_ci
1047e5c31af7Sopenharmony_ci        for param in params:
1048e5c31af7Sopenharmony_ci            self.addSharedStructMemberValidity(
1049e5c31af7Sopenharmony_ci                cmd, blockname, param, validity)
1050e5c31af7Sopenharmony_ci
1051e5c31af7Sopenharmony_ci        return validity
1052e5c31af7Sopenharmony_ci
1053e5c31af7Sopenharmony_ci    def isVKVersion11(self):
1054e5c31af7Sopenharmony_ci        """Returns true if VK_VERSION_1_1 is being emitted."""
1055e5c31af7Sopenharmony_ci        vk11 = re.match(self.registry.genOpts.emitversions, 'VK_VERSION_1_1') is not None
1056e5c31af7Sopenharmony_ci        return vk11
1057e5c31af7Sopenharmony_ci
1058e5c31af7Sopenharmony_ci    def videocodingRequired(self):
1059e5c31af7Sopenharmony_ci        """Returns true if VK_KHR_video_queue is being emitted and thus validity
1060e5c31af7Sopenharmony_ci        with respect to the videocoding attribute should be generated."""
1061e5c31af7Sopenharmony_ci        return 'VK_KHR_video_queue' in self.registry.requiredextensions
1062e5c31af7Sopenharmony_ci
1063e5c31af7Sopenharmony_ci    def getVideocoding(self, cmd):
1064e5c31af7Sopenharmony_ci        """Returns the value of the videocoding attribute, also considering the
1065e5c31af7Sopenharmony_ci        default value when the attribute is not present."""
1066e5c31af7Sopenharmony_ci        videocoding = cmd.get('videocoding')
1067e5c31af7Sopenharmony_ci        if videocoding is None:
1068e5c31af7Sopenharmony_ci            videocoding = 'outside'
1069e5c31af7Sopenharmony_ci        return videocoding
1070e5c31af7Sopenharmony_ci
1071e5c31af7Sopenharmony_ci    def conditionallyRemoveQueueType(self, queues, queuetype, condition):
1072e5c31af7Sopenharmony_ci        """Removes a queue type from a queue list based on the specified condition."""
1073e5c31af7Sopenharmony_ci        if queuetype in queues and condition:
1074e5c31af7Sopenharmony_ci            queues.remove(queuetype)
1075e5c31af7Sopenharmony_ci
1076e5c31af7Sopenharmony_ci    def getQueueList(self, cmd):
1077e5c31af7Sopenharmony_ci        """Returns the list of queue types a command is supported on."""
1078e5c31af7Sopenharmony_ci        queues = cmd.get('queues')
1079e5c31af7Sopenharmony_ci        if queues is None:
1080e5c31af7Sopenharmony_ci            return None
1081e5c31af7Sopenharmony_ci        queues = queues.split(',')
1082e5c31af7Sopenharmony_ci
1083e5c31af7Sopenharmony_ci        # Filter queue types that have dependencies
1084e5c31af7Sopenharmony_ci        self.conditionallyRemoveQueueType(queues, 'sparse_binding', self.conventions.xml_api_name == "vulkansc")
1085e5c31af7Sopenharmony_ci        self.conditionallyRemoveQueueType(queues, 'decode',         'VK_KHR_video_decode_queue' not in self.registry.requiredextensions)
1086e5c31af7Sopenharmony_ci        self.conditionallyRemoveQueueType(queues, 'encode',         'VK_KHR_video_encode_queue' not in self.registry.requiredextensions)
1087e5c31af7Sopenharmony_ci        self.conditionallyRemoveQueueType(queues, 'opticalflow',    'VK_NV_optical_flow' not in self.registry.requiredextensions)
1088e5c31af7Sopenharmony_ci
1089e5c31af7Sopenharmony_ci        # Verify that no new queue type is introduced accidentally
1090e5c31af7Sopenharmony_ci        for queue in queues:
1091e5c31af7Sopenharmony_ci            if queue not in [ 'transfer', 'compute', 'graphics', 'sparse_binding', 'decode', 'encode', 'opticalflow' ]:
1092e5c31af7Sopenharmony_ci                self.logMsg('error', f'Unknown queue type "{queue}".')
1093e5c31af7Sopenharmony_ci
1094e5c31af7Sopenharmony_ci        return queues
1095e5c31af7Sopenharmony_ci
1096e5c31af7Sopenharmony_ci    def getPrettyQueueList(self, cmd):
1097e5c31af7Sopenharmony_ci        """Returns a prettified version of the queue list which can be included in spec language text."""
1098e5c31af7Sopenharmony_ci        queues = self.getQueueList(cmd)
1099e5c31af7Sopenharmony_ci        if queues is None:
1100e5c31af7Sopenharmony_ci            return None
1101e5c31af7Sopenharmony_ci
1102e5c31af7Sopenharmony_ci        replace = {
1103e5c31af7Sopenharmony_ci            'sparse_binding': 'sparse binding',
1104e5c31af7Sopenharmony_ci            'opticalflow': 'optical flow'
1105e5c31af7Sopenharmony_ci        }
1106e5c31af7Sopenharmony_ci        return [replace[queue] if queue in replace else queue for queue in queues]
1107e5c31af7Sopenharmony_ci
1108e5c31af7Sopenharmony_ci    def makeStructOrCommandValidity(self, cmd, blockname, params):
1109e5c31af7Sopenharmony_ci        """Generate all the valid usage information for a given struct or command."""
1110e5c31af7Sopenharmony_ci        validity = self.makeValidityCollection(blockname)
1111e5c31af7Sopenharmony_ci        handles = []
1112e5c31af7Sopenharmony_ci        arraylengths = dict()
1113e5c31af7Sopenharmony_ci        for param in params:
1114e5c31af7Sopenharmony_ci            param_name = getElemName(param)
1115e5c31af7Sopenharmony_ci            paramtype = getElemType(param)
1116e5c31af7Sopenharmony_ci
1117e5c31af7Sopenharmony_ci            # Valid usage ID tags (VUID) are generated for various
1118e5c31af7Sopenharmony_ci            # conditions based on the name of the block (structure or
1119e5c31af7Sopenharmony_ci            # command), name of the element (member or parameter), and type
1120e5c31af7Sopenharmony_ci            # of VU statement.
1121e5c31af7Sopenharmony_ci
1122e5c31af7Sopenharmony_ci            # Get the type's category
1123e5c31af7Sopenharmony_ci            typecategory = self.getTypeCategory(paramtype)
1124e5c31af7Sopenharmony_ci
1125e5c31af7Sopenharmony_ci            if not self.addSharedStructMemberValidity(
1126e5c31af7Sopenharmony_ci                    cmd, blockname, param, validity):
1127e5c31af7Sopenharmony_ci                if not param.get('selector'):
1128e5c31af7Sopenharmony_ci                    validity += self.createValidationLineForParameter(
1129e5c31af7Sopenharmony_ci                        blockname, param, params, typecategory, None, None)
1130e5c31af7Sopenharmony_ci                else:
1131e5c31af7Sopenharmony_ci                    selector = param.get('selector')
1132e5c31af7Sopenharmony_ci                    if typecategory != 'union':
1133e5c31af7Sopenharmony_ci                        self.logMsg('warn', 'selector attribute set on non-union parameter', param_name, 'in', blockname)
1134e5c31af7Sopenharmony_ci
1135e5c31af7Sopenharmony_ci                    paraminfo = self.registry.lookupElementInfo(paramtype, self.registry.typedict)
1136e5c31af7Sopenharmony_ci
1137e5c31af7Sopenharmony_ci                    for member in paraminfo.getMembers():
1138e5c31af7Sopenharmony_ci                        membertype = getElemType(member)
1139e5c31af7Sopenharmony_ci                        membertypecategory = self.getTypeCategory(membertype)
1140e5c31af7Sopenharmony_ci
1141e5c31af7Sopenharmony_ci                        validity += self.createValidationLineForParameter(
1142e5c31af7Sopenharmony_ci                            blockname, member, paraminfo.getMembers(), membertypecategory, selector, param_name)
1143e5c31af7Sopenharmony_ci
1144e5c31af7Sopenharmony_ci            # Ensure that any parenting is properly validated, and list that a handle was found
1145e5c31af7Sopenharmony_ci            if typecategory == 'handle':
1146e5c31af7Sopenharmony_ci                handles.append(param)
1147e5c31af7Sopenharmony_ci
1148e5c31af7Sopenharmony_ci            # Get the array length for this parameter
1149e5c31af7Sopenharmony_ci            lengths = LengthEntry.parse_len_from_param(param)
1150e5c31af7Sopenharmony_ci            if lengths:
1151e5c31af7Sopenharmony_ci                arraylengths.update({length.other_param_name: length
1152e5c31af7Sopenharmony_ci                                     for length in lengths
1153e5c31af7Sopenharmony_ci                                     if length.other_param_name})
1154e5c31af7Sopenharmony_ci
1155e5c31af7Sopenharmony_ci        # For any vkQueue* functions, there might be queue type data
1156e5c31af7Sopenharmony_ci        if 'vkQueue' in blockname:
1157e5c31af7Sopenharmony_ci            # The queue type must be valid
1158e5c31af7Sopenharmony_ci            queues = self.getPrettyQueueList(cmd)
1159e5c31af7Sopenharmony_ci            if queues:
1160e5c31af7Sopenharmony_ci                entry = ValidityEntry(anchor=('queuetype',))
1161e5c31af7Sopenharmony_ci                entry += 'The pname:queue must: support '
1162e5c31af7Sopenharmony_ci                entry += self.makeProseList(queues,
1163e5c31af7Sopenharmony_ci                                            fmt=plf.OR, comma_for_two_elts=True)
1164e5c31af7Sopenharmony_ci                entry += ' operations'
1165e5c31af7Sopenharmony_ci                validity += entry
1166e5c31af7Sopenharmony_ci
1167e5c31af7Sopenharmony_ci        if 'vkCmd' in blockname:
1168e5c31af7Sopenharmony_ci            # The commandBuffer parameter must be being recorded
1169e5c31af7Sopenharmony_ci            entry = ValidityEntry(anchor=('commandBuffer', 'recording'))
1170e5c31af7Sopenharmony_ci            entry += 'pname:commandBuffer must: be in the <<commandbuffers-lifecycle, recording state>>'
1171e5c31af7Sopenharmony_ci            validity += entry
1172e5c31af7Sopenharmony_ci
1173e5c31af7Sopenharmony_ci            #
1174e5c31af7Sopenharmony_ci            # Start of valid queue type validation - command pool must have been
1175e5c31af7Sopenharmony_ci            # allocated against a queue with at least one of the valid queue types
1176e5c31af7Sopenharmony_ci            entry = ValidityEntry(anchor=('commandBuffer', 'cmdpool'))
1177e5c31af7Sopenharmony_ci
1178e5c31af7Sopenharmony_ci            #
1179e5c31af7Sopenharmony_ci            # This test for vkCmdFillBuffer is a hack, since we have no path
1180e5c31af7Sopenharmony_ci            # to conditionally have queues enabled or disabled by an extension.
1181e5c31af7Sopenharmony_ci            # As the VU stuff is all moving out (hopefully soon), this hack solves the issue for now
1182e5c31af7Sopenharmony_ci            if blockname == 'vkCmdFillBuffer':
1183e5c31af7Sopenharmony_ci                entry += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support '
1184e5c31af7Sopenharmony_ci                if self.isVKVersion11() or 'VK_KHR_maintenance1' in self.registry.requiredextensions:
1185e5c31af7Sopenharmony_ci                    entry += 'transfer, graphics or compute operations'
1186e5c31af7Sopenharmony_ci                else:
1187e5c31af7Sopenharmony_ci                    entry += 'graphics or compute operations'
1188e5c31af7Sopenharmony_ci            else:
1189e5c31af7Sopenharmony_ci                # The queue type must be valid
1190e5c31af7Sopenharmony_ci                queues = self.getPrettyQueueList(cmd)
1191e5c31af7Sopenharmony_ci                assert(queues)
1192e5c31af7Sopenharmony_ci                entry += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support '
1193e5c31af7Sopenharmony_ci                entry += self.makeProseList(queues,
1194e5c31af7Sopenharmony_ci                                            fmt=plf.OR, comma_for_two_elts=True)
1195e5c31af7Sopenharmony_ci                entry += ' operations'
1196e5c31af7Sopenharmony_ci            validity += entry
1197e5c31af7Sopenharmony_ci
1198e5c31af7Sopenharmony_ci            # Must be called inside/outside a render pass appropriately
1199e5c31af7Sopenharmony_ci            renderpass = cmd.get('renderpass')
1200e5c31af7Sopenharmony_ci
1201e5c31af7Sopenharmony_ci            if renderpass != 'both':
1202e5c31af7Sopenharmony_ci                entry = ValidityEntry(anchor=('renderpass',))
1203e5c31af7Sopenharmony_ci                entry += 'This command must: only be called '
1204e5c31af7Sopenharmony_ci                entry += renderpass
1205e5c31af7Sopenharmony_ci                entry += ' of a render pass instance'
1206e5c31af7Sopenharmony_ci                validity += entry
1207e5c31af7Sopenharmony_ci
1208e5c31af7Sopenharmony_ci            # Must be called inside/outside a video coding scope appropriately
1209e5c31af7Sopenharmony_ci            if self.videocodingRequired():
1210e5c31af7Sopenharmony_ci                videocoding = self.getVideocoding(cmd)
1211e5c31af7Sopenharmony_ci                if videocoding != 'both':
1212e5c31af7Sopenharmony_ci                    entry = ValidityEntry(anchor=('videocoding',))
1213e5c31af7Sopenharmony_ci                    entry += 'This command must: only be called '
1214e5c31af7Sopenharmony_ci                    entry += videocoding
1215e5c31af7Sopenharmony_ci                    entry += ' of a video coding scope'
1216e5c31af7Sopenharmony_ci                    validity += entry
1217e5c31af7Sopenharmony_ci
1218e5c31af7Sopenharmony_ci            # Must be in the right level command buffer
1219e5c31af7Sopenharmony_ci            cmdbufferlevel = cmd.get('cmdbufferlevel')
1220e5c31af7Sopenharmony_ci
1221e5c31af7Sopenharmony_ci            if cmdbufferlevel != 'primary,secondary':
1222e5c31af7Sopenharmony_ci                entry = ValidityEntry(anchor=('bufferlevel',))
1223e5c31af7Sopenharmony_ci                entry += 'pname:commandBuffer must: be a '
1224e5c31af7Sopenharmony_ci                entry += cmdbufferlevel
1225e5c31af7Sopenharmony_ci                entry += ' sname:VkCommandBuffer'
1226e5c31af7Sopenharmony_ci                validity += entry
1227e5c31af7Sopenharmony_ci
1228e5c31af7Sopenharmony_ci        # Any non-optional arraylengths should specify they must be greater than 0
1229e5c31af7Sopenharmony_ci        array_length_params = ((param, getElemName(param))
1230e5c31af7Sopenharmony_ci                               for param in params
1231e5c31af7Sopenharmony_ci                               if getElemName(param) in arraylengths)
1232e5c31af7Sopenharmony_ci
1233e5c31af7Sopenharmony_ci        for param, param_name in array_length_params:
1234e5c31af7Sopenharmony_ci            if param.get('optional') is not None:
1235e5c31af7Sopenharmony_ci                continue
1236e5c31af7Sopenharmony_ci
1237e5c31af7Sopenharmony_ci            length = arraylengths[param_name]
1238e5c31af7Sopenharmony_ci            full_length = length.full_reference
1239e5c31af7Sopenharmony_ci
1240e5c31af7Sopenharmony_ci            # Is this just a name of a param? If false, then it is some kind
1241e5c31af7Sopenharmony_ci            # of qualified name (a member of a param for instance)
1242e5c31af7Sopenharmony_ci            simple_param_reference = (len(length.param_ref_parts) == 1)
1243e5c31af7Sopenharmony_ci            if not simple_param_reference:
1244e5c31af7Sopenharmony_ci                # Loop through to see if any parameters in the chain are optional
1245e5c31af7Sopenharmony_ci                array_length_parent = cmd
1246e5c31af7Sopenharmony_ci                array_length_optional = False
1247e5c31af7Sopenharmony_ci                for part in length.param_ref_parts:
1248e5c31af7Sopenharmony_ci                    # Overwrite the param so it ends up as the bottom level parameter for later checks
1249e5c31af7Sopenharmony_ci                    param = array_length_parent.find("*/[name='{}']".format(part))
1250e5c31af7Sopenharmony_ci
1251e5c31af7Sopenharmony_ci                    # If any parameter in the chain is optional, skip the implicit length requirement
1252e5c31af7Sopenharmony_ci                    array_length_optional |= (param.get('optional') is not None)
1253e5c31af7Sopenharmony_ci
1254e5c31af7Sopenharmony_ci                    # Lookup the type of the parameter for the next loop iteration
1255e5c31af7Sopenharmony_ci                    type = param.findtext('type')
1256e5c31af7Sopenharmony_ci                    array_length_parent = self.registry.tree.find("./types/type/[@name='{}']".format(type))
1257e5c31af7Sopenharmony_ci
1258e5c31af7Sopenharmony_ci                if array_length_optional:
1259e5c31af7Sopenharmony_ci                    continue
1260e5c31af7Sopenharmony_ci
1261e5c31af7Sopenharmony_ci            # Get all the array dependencies
1262e5c31af7Sopenharmony_ci            arrays = cmd.findall(
1263e5c31af7Sopenharmony_ci                "param/[@len='{}'][@optional='true']".format(full_length))
1264e5c31af7Sopenharmony_ci
1265e5c31af7Sopenharmony_ci            # Get all the optional array dependencies, including those not generating validity for some reason
1266e5c31af7Sopenharmony_ci            optionalarrays = arrays + \
1267e5c31af7Sopenharmony_ci                cmd.findall(
1268e5c31af7Sopenharmony_ci                    "param/[@len='{}'][@noautovalidity='true']".format(full_length))
1269e5c31af7Sopenharmony_ci
1270e5c31af7Sopenharmony_ci            entry = ValidityEntry(anchor=(full_length, 'arraylength'))
1271e5c31af7Sopenharmony_ci            # Allow lengths to be arbitrary if all their dependents are optional
1272e5c31af7Sopenharmony_ci            if optionalarrays and len(optionalarrays) == len(arrays):
1273e5c31af7Sopenharmony_ci                entry += 'If '
1274e5c31af7Sopenharmony_ci                # TODO sync this section from OpenXR once cosmetic changes OK
1275e5c31af7Sopenharmony_ci
1276e5c31af7Sopenharmony_ci                optional_array_names = (self.makeParameterName(getElemName(array))
1277e5c31af7Sopenharmony_ci                                        for array in optionalarrays)
1278e5c31af7Sopenharmony_ci                entry += self.makeProseListIs(optional_array_names,
1279e5c31af7Sopenharmony_ci                                              plf.ANY_OR, comma_for_two_elts=True)
1280e5c31af7Sopenharmony_ci
1281e5c31af7Sopenharmony_ci                entry += ' not {}, '.format(self.null)
1282e5c31af7Sopenharmony_ci
1283e5c31af7Sopenharmony_ci            # TODO end needs sync cosmetic
1284e5c31af7Sopenharmony_ci            if self.paramIsPointer(param):
1285e5c31af7Sopenharmony_ci                entry += 'the value referenced by '
1286e5c31af7Sopenharmony_ci
1287e5c31af7Sopenharmony_ci            # Split and re-join here to insert pname: around ::
1288e5c31af7Sopenharmony_ci            entry += '::'.join(self.makeParameterName(part)
1289e5c31af7Sopenharmony_ci                               for part in full_length.split('::'))
1290e5c31af7Sopenharmony_ci            # TODO replace the previous statement with the following when cosmetic changes OK
1291e5c31af7Sopenharmony_ci            # entry += length.get_human_readable(make_param_name=self.makeParameterName)
1292e5c31af7Sopenharmony_ci
1293e5c31af7Sopenharmony_ci            entry += ' must: be greater than '
1294e5c31af7Sopenharmony_ci            entry += self.conventions.zero
1295e5c31af7Sopenharmony_ci            validity += entry
1296e5c31af7Sopenharmony_ci
1297e5c31af7Sopenharmony_ci        # Find the parents of all objects referenced in this command
1298e5c31af7Sopenharmony_ci        for param in handles:
1299e5c31af7Sopenharmony_ci            # Do not detect a parent for return values!
1300e5c31af7Sopenharmony_ci            if not self.paramIsPointer(param) or self.paramIsConst(param):
1301e5c31af7Sopenharmony_ci                validity += self.makeHandleValidityParent(param, params)
1302e5c31af7Sopenharmony_ci
1303e5c31af7Sopenharmony_ci        # Find the common ancestor of all objects referenced in this command
1304e5c31af7Sopenharmony_ci        validity += self.makeAsciiDocHandlesCommonAncestor(
1305e5c31af7Sopenharmony_ci            blockname, handles, params)
1306e5c31af7Sopenharmony_ci
1307e5c31af7Sopenharmony_ci        return validity
1308e5c31af7Sopenharmony_ci
1309e5c31af7Sopenharmony_ci    def makeThreadSafetyBlock(self, cmd, paramtext):
1310e5c31af7Sopenharmony_ci        """Generate thread-safety validity entries for cmd/structure"""
1311e5c31af7Sopenharmony_ci        # See also makeThreadSafetyBlock in validitygenerator.py
1312e5c31af7Sopenharmony_ci        validity = self.makeValidityCollection(getElemName(cmd))
1313e5c31af7Sopenharmony_ci
1314e5c31af7Sopenharmony_ci        # This text varies between projects, so an Asciidoctor attribute is used.
1315e5c31af7Sopenharmony_ci        extsync_prefix = "{externsyncprefix} "
1316e5c31af7Sopenharmony_ci
1317e5c31af7Sopenharmony_ci        # Find and add any parameters that are thread unsafe
1318e5c31af7Sopenharmony_ci        explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]")
1319e5c31af7Sopenharmony_ci        if explicitexternsyncparams is not None:
1320e5c31af7Sopenharmony_ci            for param in explicitexternsyncparams:
1321e5c31af7Sopenharmony_ci                externsyncattribs = ExternSyncEntry.parse_externsync_from_param(
1322e5c31af7Sopenharmony_ci                    param)
1323e5c31af7Sopenharmony_ci                param_name = getElemName(param)
1324e5c31af7Sopenharmony_ci
1325e5c31af7Sopenharmony_ci                for attrib in externsyncattribs:
1326e5c31af7Sopenharmony_ci                    entry = ValidityEntry()
1327e5c31af7Sopenharmony_ci                    entry += extsync_prefix
1328e5c31af7Sopenharmony_ci                    if attrib.entirely_extern_sync:
1329e5c31af7Sopenharmony_ci                        if self.paramIsArray(param):
1330e5c31af7Sopenharmony_ci                            entry += 'each member of '
1331e5c31af7Sopenharmony_ci                        elif self.paramIsPointer(param):
1332e5c31af7Sopenharmony_ci                            entry += 'the object referenced by '
1333e5c31af7Sopenharmony_ci
1334e5c31af7Sopenharmony_ci                        entry += self.makeParameterName(param_name)
1335e5c31af7Sopenharmony_ci
1336e5c31af7Sopenharmony_ci                        if attrib.children_extern_sync:
1337e5c31af7Sopenharmony_ci                            entry += ', and any child handles,'
1338e5c31af7Sopenharmony_ci
1339e5c31af7Sopenharmony_ci                    else:
1340e5c31af7Sopenharmony_ci                        entry += 'pname:'
1341e5c31af7Sopenharmony_ci                        entry += str(attrib.full_reference)
1342e5c31af7Sopenharmony_ci                        # TODO switch to the following when cosmetic changes OK
1343e5c31af7Sopenharmony_ci                        # entry += attrib.get_human_readable(make_param_name=self.makeParameterName)
1344e5c31af7Sopenharmony_ci                    entry += ' must: be externally synchronized'
1345e5c31af7Sopenharmony_ci                    validity += entry
1346e5c31af7Sopenharmony_ci
1347e5c31af7Sopenharmony_ci        # Vulkan-specific
1348e5c31af7Sopenharmony_ci        # For any vkCmd* functions, the command pool is externally synchronized
1349e5c31af7Sopenharmony_ci        if cmd.find('proto/name') is not None and 'vkCmd' in cmd.find('proto/name').text:
1350e5c31af7Sopenharmony_ci            entry = ValidityEntry()
1351e5c31af7Sopenharmony_ci            entry += extsync_prefix
1352e5c31af7Sopenharmony_ci            entry += 'the sname:VkCommandPool that pname:commandBuffer was allocated from must: be externally synchronized'
1353e5c31af7Sopenharmony_ci            validity += entry
1354e5c31af7Sopenharmony_ci
1355e5c31af7Sopenharmony_ci        # Find and add any "implicit" parameters that are thread unsafe
1356e5c31af7Sopenharmony_ci        implicitexternsyncparams = cmd.find('implicitexternsyncparams')
1357e5c31af7Sopenharmony_ci        if implicitexternsyncparams is not None:
1358e5c31af7Sopenharmony_ci            for elem in implicitexternsyncparams:
1359e5c31af7Sopenharmony_ci                entry = ValidityEntry()
1360e5c31af7Sopenharmony_ci                entry += extsync_prefix
1361e5c31af7Sopenharmony_ci                entry += elem.text
1362e5c31af7Sopenharmony_ci                entry += ' must: be externally synchronized'
1363e5c31af7Sopenharmony_ci                validity += entry
1364e5c31af7Sopenharmony_ci
1365e5c31af7Sopenharmony_ci        return validity
1366e5c31af7Sopenharmony_ci
1367e5c31af7Sopenharmony_ci    def makeCommandPropertiesTableHeader(self):
1368e5c31af7Sopenharmony_ci        header  = '|<<VkCommandBufferLevel,Command Buffer Levels>>'
1369e5c31af7Sopenharmony_ci        header += '|<<vkCmdBeginRenderPass,Render Pass Scope>>'
1370e5c31af7Sopenharmony_ci        if self.videocodingRequired():
1371e5c31af7Sopenharmony_ci            header += '|<<vkCmdBeginVideoCodingKHR,Video Coding Scope>>'
1372e5c31af7Sopenharmony_ci        header += '|<<VkQueueFlagBits,Supported Queue Types>>'
1373e5c31af7Sopenharmony_ci        header += '|<<fundamentals-queueoperation-command-types,Command Type>>'
1374e5c31af7Sopenharmony_ci        return header
1375e5c31af7Sopenharmony_ci
1376e5c31af7Sopenharmony_ci    def makeCommandPropertiesTableEntry(self, cmd, name):
1377e5c31af7Sopenharmony_ci        cmdbufferlevel, renderpass, videocoding, queues, tasks = None, None, None, None, None
1378e5c31af7Sopenharmony_ci
1379e5c31af7Sopenharmony_ci        if 'vkCmd' in name:
1380e5c31af7Sopenharmony_ci            # Must be called in primary/secondary command buffers appropriately
1381e5c31af7Sopenharmony_ci            cmdbufferlevel = cmd.get('cmdbufferlevel')
1382e5c31af7Sopenharmony_ci            cmdbufferlevel = (' + \n').join(cmdbufferlevel.title().split(','))
1383e5c31af7Sopenharmony_ci
1384e5c31af7Sopenharmony_ci            # Must be called inside/outside a render pass appropriately
1385e5c31af7Sopenharmony_ci            renderpass = cmd.get('renderpass')
1386e5c31af7Sopenharmony_ci            renderpass = renderpass.capitalize()
1387e5c31af7Sopenharmony_ci
1388e5c31af7Sopenharmony_ci            # Must be called inside/outside a video coding scope appropriately
1389e5c31af7Sopenharmony_ci            if self.videocodingRequired():
1390e5c31af7Sopenharmony_ci                videocoding = self.getVideocoding(cmd).capitalize()
1391e5c31af7Sopenharmony_ci
1392e5c31af7Sopenharmony_ci            #
1393e5c31af7Sopenharmony_ci            # This test for vkCmdFillBuffer is a hack, since we have no path
1394e5c31af7Sopenharmony_ci            # to conditionally have queues enabled or disabled by an extension.
1395e5c31af7Sopenharmony_ci            # As the VU stuff is all moving out (hopefully soon), this hack solves the issue for now
1396e5c31af7Sopenharmony_ci            if name == 'vkCmdFillBuffer':
1397e5c31af7Sopenharmony_ci                if self.isVKVersion11() or 'VK_KHR_maintenance1' in self.registry.requiredextensions:
1398e5c31af7Sopenharmony_ci                    queues = [ 'transfer', 'graphics', 'compute' ]
1399e5c31af7Sopenharmony_ci                else:
1400e5c31af7Sopenharmony_ci                    queues = [ 'graphics', 'compute' ]
1401e5c31af7Sopenharmony_ci            else:
1402e5c31af7Sopenharmony_ci                queues = self.getQueueList(cmd)
1403e5c31af7Sopenharmony_ci            queues = (' + \n').join([queue.title() for queue in queues])
1404e5c31af7Sopenharmony_ci
1405e5c31af7Sopenharmony_ci            tasks = cmd.get('tasks')
1406e5c31af7Sopenharmony_ci            tasks = (' + \n').join(tasks.title().split(','))
1407e5c31af7Sopenharmony_ci        elif 'vkQueue' in name:
1408e5c31af7Sopenharmony_ci            # For queue commands there are no command buffer level, render
1409e5c31af7Sopenharmony_ci            # pass, or video coding scope specific restrictions,
1410e5c31af7Sopenharmony_ci            # or command type, but the queue types are considered
1411e5c31af7Sopenharmony_ci            cmdbufferlevel = '-'
1412e5c31af7Sopenharmony_ci            renderpass = '-'
1413e5c31af7Sopenharmony_ci            if self.videocodingRequired():
1414e5c31af7Sopenharmony_ci                videocoding = '-'
1415e5c31af7Sopenharmony_ci
1416e5c31af7Sopenharmony_ci            queues = self.getQueueList(cmd)
1417e5c31af7Sopenharmony_ci            if queues is None:
1418e5c31af7Sopenharmony_ci                queues = 'Any'
1419e5c31af7Sopenharmony_ci            else:
1420e5c31af7Sopenharmony_ci                queues = (' + \n').join([queue.upper() for queue in queues])
1421e5c31af7Sopenharmony_ci
1422e5c31af7Sopenharmony_ci            tasks = '-'
1423e5c31af7Sopenharmony_ci
1424e5c31af7Sopenharmony_ci        table_items = (cmdbufferlevel, renderpass, videocoding, queues, tasks)
1425e5c31af7Sopenharmony_ci        entry = '|'.join(filter(None, table_items))
1426e5c31af7Sopenharmony_ci
1427e5c31af7Sopenharmony_ci        return ('|' + entry) if entry else None
1428e5c31af7Sopenharmony_ci
1429e5c31af7Sopenharmony_ci
1430e5c31af7Sopenharmony_ci    def findRequiredEnums(self, enums):
1431e5c31af7Sopenharmony_ci        """Check each enumerant name in the enums list and remove it if not
1432e5c31af7Sopenharmony_ci        required by the generator. This allows specifying success and error
1433e5c31af7Sopenharmony_ci        codes for extensions that are only included in validity when needed
1434e5c31af7Sopenharmony_ci        for the spec being targeted."""
1435e5c31af7Sopenharmony_ci        return self.keepOnlyRequired(enums, self.registry.enumdict)
1436e5c31af7Sopenharmony_ci
1437e5c31af7Sopenharmony_ci    def findRequiredCommands(self, commands):
1438e5c31af7Sopenharmony_ci        """Check each command name in the commands list and remove it if not
1439e5c31af7Sopenharmony_ci        required by the generator.
1440e5c31af7Sopenharmony_ci
1441e5c31af7Sopenharmony_ci        This will allow some state operations to take place before endFile."""
1442e5c31af7Sopenharmony_ci        return self.keepOnlyRequired(commands, self.registry.cmddict)
1443e5c31af7Sopenharmony_ci
1444e5c31af7Sopenharmony_ci    def keepOnlyRequired(self, names, info_dict):
1445e5c31af7Sopenharmony_ci        """Check each element name in the supplied dictionary and remove it if not
1446e5c31af7Sopenharmony_ci        required by the generator.
1447e5c31af7Sopenharmony_ci
1448e5c31af7Sopenharmony_ci        This will allow some operations to take place before endFile no matter the order of generation."""
1449e5c31af7Sopenharmony_ci        # TODO Unpleasantly breaks encapsulation. Should be a method in the registry class
1450e5c31af7Sopenharmony_ci
1451e5c31af7Sopenharmony_ci        def is_required(name):
1452e5c31af7Sopenharmony_ci            info = self.registry.lookupElementInfo(name, info_dict)
1453e5c31af7Sopenharmony_ci            if info is None:
1454e5c31af7Sopenharmony_ci                return False
1455e5c31af7Sopenharmony_ci            if not info.required:
1456e5c31af7Sopenharmony_ci                self.logMsg('diag', 'keepOnlyRequired: element',
1457e5c31af7Sopenharmony_ci                            name, 'IS NOT required, skipping')
1458e5c31af7Sopenharmony_ci            return info.required
1459e5c31af7Sopenharmony_ci
1460e5c31af7Sopenharmony_ci        return [name
1461e5c31af7Sopenharmony_ci                for name in names
1462e5c31af7Sopenharmony_ci                if is_required(name)]
1463e5c31af7Sopenharmony_ci
1464e5c31af7Sopenharmony_ci    def makeReturnCodeList(self, attrib, cmd, name):
1465e5c31af7Sopenharmony_ci        """Return a list of possible return codes for a function.
1466e5c31af7Sopenharmony_ci
1467e5c31af7Sopenharmony_ci        attrib is either 'successcodes' or 'errorcodes'.
1468e5c31af7Sopenharmony_ci        """
1469e5c31af7Sopenharmony_ci        return_lines = []
1470e5c31af7Sopenharmony_ci        RETURN_CODE_FORMAT = '* ename:{}'
1471e5c31af7Sopenharmony_ci
1472e5c31af7Sopenharmony_ci        codes_attr = cmd.get(attrib)
1473e5c31af7Sopenharmony_ci        if codes_attr:
1474e5c31af7Sopenharmony_ci            codes = self.findRequiredEnums(codes_attr.split(','))
1475e5c31af7Sopenharmony_ci            if codes:
1476e5c31af7Sopenharmony_ci                return_lines.extend((RETURN_CODE_FORMAT.format(code)
1477e5c31af7Sopenharmony_ci                                     for code in codes))
1478e5c31af7Sopenharmony_ci
1479e5c31af7Sopenharmony_ci        applicable_ext_codes = (ext_code
1480e5c31af7Sopenharmony_ci                                for ext_code in self.registry.commandextensionsuccesses
1481e5c31af7Sopenharmony_ci                                if ext_code.command == name)
1482e5c31af7Sopenharmony_ci        for ext_code in applicable_ext_codes:
1483e5c31af7Sopenharmony_ci            line = RETURN_CODE_FORMAT.format(ext_code.value)
1484e5c31af7Sopenharmony_ci            if ext_code.extension:
1485e5c31af7Sopenharmony_ci                line += ' [only if {} is enabled]'.format(
1486e5c31af7Sopenharmony_ci                    self.conventions.formatExtension(ext_code.extension))
1487e5c31af7Sopenharmony_ci
1488e5c31af7Sopenharmony_ci            return_lines.append(line)
1489e5c31af7Sopenharmony_ci        if return_lines:
1490e5c31af7Sopenharmony_ci            return '\n'.join(return_lines)
1491e5c31af7Sopenharmony_ci
1492e5c31af7Sopenharmony_ci        return None
1493e5c31af7Sopenharmony_ci
1494e5c31af7Sopenharmony_ci    def makeSuccessCodes(self, cmd, name):
1495e5c31af7Sopenharmony_ci        return self.makeReturnCodeList('successcodes', cmd, name)
1496e5c31af7Sopenharmony_ci
1497e5c31af7Sopenharmony_ci    def makeErrorCodes(self, cmd, name):
1498e5c31af7Sopenharmony_ci        return self.makeReturnCodeList('errorcodes', cmd, name)
1499e5c31af7Sopenharmony_ci
1500e5c31af7Sopenharmony_ci    def genCmd(self, cmdinfo, name, alias):
1501e5c31af7Sopenharmony_ci        """Command generation."""
1502e5c31af7Sopenharmony_ci        OutputGenerator.genCmd(self, cmdinfo, name, alias)
1503e5c31af7Sopenharmony_ci
1504e5c31af7Sopenharmony_ci        # @@@ (Jon) something needs to be done here to handle aliases, probably
1505e5c31af7Sopenharmony_ci
1506e5c31af7Sopenharmony_ci        validity = self.makeValidityCollection(name)
1507e5c31af7Sopenharmony_ci
1508e5c31af7Sopenharmony_ci        # OpenXR-only: make sure extension is enabled
1509e5c31af7Sopenharmony_ci        # validity.possiblyAddExtensionRequirement(self.currentExtension, 'calling flink:')
1510e5c31af7Sopenharmony_ci
1511e5c31af7Sopenharmony_ci        validity += self.makeStructOrCommandValidity(
1512e5c31af7Sopenharmony_ci            cmdinfo.elem, name, cmdinfo.getParams())
1513e5c31af7Sopenharmony_ci
1514e5c31af7Sopenharmony_ci        threadsafety = self.makeThreadSafetyBlock(cmdinfo.elem, 'param')
1515e5c31af7Sopenharmony_ci        commandpropertiesentry = None
1516e5c31af7Sopenharmony_ci
1517e5c31af7Sopenharmony_ci        # Vulkan-specific
1518e5c31af7Sopenharmony_ci        commandpropertiesentry = self.makeCommandPropertiesTableEntry(
1519e5c31af7Sopenharmony_ci            cmdinfo.elem, name)
1520e5c31af7Sopenharmony_ci        successcodes = self.makeSuccessCodes(cmdinfo.elem, name)
1521e5c31af7Sopenharmony_ci        errorcodes = self.makeErrorCodes(cmdinfo.elem, name)
1522e5c31af7Sopenharmony_ci
1523e5c31af7Sopenharmony_ci        # OpenXR-specific
1524e5c31af7Sopenharmony_ci        # self.generateStateValidity(validity, name)
1525e5c31af7Sopenharmony_ci
1526e5c31af7Sopenharmony_ci        self.writeInclude('protos', name, validity, threadsafety,
1527e5c31af7Sopenharmony_ci                          commandpropertiesentry, successcodes, errorcodes)
1528e5c31af7Sopenharmony_ci
1529e5c31af7Sopenharmony_ci    def genStruct(self, typeinfo, typeName, alias):
1530e5c31af7Sopenharmony_ci        """Struct Generation."""
1531e5c31af7Sopenharmony_ci        OutputGenerator.genStruct(self, typeinfo, typeName, alias)
1532e5c31af7Sopenharmony_ci
1533e5c31af7Sopenharmony_ci        # @@@ (Jon) something needs to be done here to handle aliases, probably
1534e5c31af7Sopenharmony_ci
1535e5c31af7Sopenharmony_ci        # Anything that is only ever returned cannot be set by the user, so
1536e5c31af7Sopenharmony_ci        # should not have any validity information.
1537e5c31af7Sopenharmony_ci        validity = self.makeValidityCollection(typeName)
1538e5c31af7Sopenharmony_ci        threadsafety = []
1539e5c31af7Sopenharmony_ci
1540e5c31af7Sopenharmony_ci        # OpenXR-only: make sure extension is enabled
1541e5c31af7Sopenharmony_ci        # validity.possiblyAddExtensionRequirement(self.currentExtension, 'using slink:')
1542e5c31af7Sopenharmony_ci
1543e5c31af7Sopenharmony_ci        if typeinfo.elem.get('category') != 'union':
1544e5c31af7Sopenharmony_ci            if typeinfo.elem.get('returnedonly') is None:
1545e5c31af7Sopenharmony_ci                validity += self.makeStructOrCommandValidity(
1546e5c31af7Sopenharmony_ci                    typeinfo.elem, typeName, typeinfo.getMembers())
1547e5c31af7Sopenharmony_ci                threadsafety = self.makeThreadSafetyBlock(typeinfo.elem, 'member')
1548e5c31af7Sopenharmony_ci
1549e5c31af7Sopenharmony_ci            else:
1550e5c31af7Sopenharmony_ci                # Need to generate structure type and next pointer chain member validation
1551e5c31af7Sopenharmony_ci                validity += self.makeOutputOnlyStructValidity(
1552e5c31af7Sopenharmony_ci                    typeinfo.elem, typeName, typeinfo.getMembers())
1553e5c31af7Sopenharmony_ci
1554e5c31af7Sopenharmony_ci        self.writeInclude('structs', typeName, validity,
1555e5c31af7Sopenharmony_ci                          threadsafety, None, None, None)
1556e5c31af7Sopenharmony_ci
1557e5c31af7Sopenharmony_ci    def genGroup(self, groupinfo, groupName, alias):
1558e5c31af7Sopenharmony_ci        """Group (e.g. C "enum" type) generation.
1559e5c31af7Sopenharmony_ci        For the validity generator, this just tags individual enumerants
1560e5c31af7Sopenharmony_ci        as required or not.
1561e5c31af7Sopenharmony_ci        """
1562e5c31af7Sopenharmony_ci        OutputGenerator.genGroup(self, groupinfo, groupName, alias)
1563e5c31af7Sopenharmony_ci
1564e5c31af7Sopenharmony_ci        # @@@ (Jon) something needs to be done here to handle aliases, probably
1565e5c31af7Sopenharmony_ci
1566e5c31af7Sopenharmony_ci        groupElem = groupinfo.elem
1567e5c31af7Sopenharmony_ci
1568e5c31af7Sopenharmony_ci        # Loop over the nested 'enum' tags. Keep track of the minimum and
1569e5c31af7Sopenharmony_ci        # maximum numeric values, if they can be determined; but only for
1570e5c31af7Sopenharmony_ci        # core API enumerants, not extension enumerants. This is inferred
1571e5c31af7Sopenharmony_ci        # by looking for 'extends' attributes.
1572e5c31af7Sopenharmony_ci        for elem in groupElem.findall('enum'):
1573e5c31af7Sopenharmony_ci            name = elem.get('name')
1574e5c31af7Sopenharmony_ci            ei = self.registry.lookupElementInfo(name, self.registry.enumdict)
1575e5c31af7Sopenharmony_ci
1576e5c31af7Sopenharmony_ci            if ei is None:
1577e5c31af7Sopenharmony_ci                self.logMsg('error',
1578e5c31af7Sopenharmony_ci                    f'genGroup({groupName}) - no element found for enum {name}')
1579e5c31af7Sopenharmony_ci
1580e5c31af7Sopenharmony_ci            # Tag enumerant as required or not
1581e5c31af7Sopenharmony_ci            ei.required = self.isEnumRequired(elem)
1582e5c31af7Sopenharmony_ci
1583e5c31af7Sopenharmony_ci    def genType(self, typeinfo, name, alias):
1584e5c31af7Sopenharmony_ci        """Type Generation."""
1585e5c31af7Sopenharmony_ci        OutputGenerator.genType(self, typeinfo, name, alias)
1586e5c31af7Sopenharmony_ci
1587e5c31af7Sopenharmony_ci        # @@@ (Jon) something needs to be done here to handle aliases, probably
1588e5c31af7Sopenharmony_ci
1589e5c31af7Sopenharmony_ci        category = typeinfo.elem.get('category')
1590e5c31af7Sopenharmony_ci        if category in ('struct', 'union'):
1591e5c31af7Sopenharmony_ci            self.genStruct(typeinfo, name, alias)
1592