• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3 -i
2#
3# Copyright (c) 2015-2019 The Khronos Group Inc.
4# Copyright (c) 2015-2019 Valve Corporation
5# Copyright (c) 2015-2019 LunarG, Inc.
6# Copyright (c) 2015-2019 Google Inc.
7#
8# Licensed under the Apache License, Version 2.0 (the "License");
9# you may not use this file except in compliance with the License.
10# You may obtain a copy of the License at
11#
12#     http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS,
16# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17# See the License for the specific language governing permissions and
18# limitations under the License.
19#
20# Author: Dustin Graves <dustin@lunarg.com>
21# Author: Mark Lobodzinski <mark@lunarg.com>
22# Author: Dave Houlton <daveh@lunarg.com>
23
24import os,re,sys,string,json
25import xml.etree.ElementTree as etree
26from generator import *
27from collections import namedtuple
28from common_codegen import *
29
30# This is a workaround to use a Python 2.7 and 3.x compatible syntax.
31from io import open
32
33# ParameterValidationGeneratorOptions - subclass of GeneratorOptions.
34#
35# Adds options used by ParameterValidationOutputGenerator object during Parameter validation layer generation.
36#
37# Additional members
38#   prefixText - list of strings to prefix generated header with
39#     (usually a copyright statement + calling convention macros).
40#   protectFile - True if multiple inclusion protection should be
41#     generated (based on the filename) around the entire header.
42#   protectFeature - True if #ifndef..#endif protection should be
43#     generated around a feature interface in the header file.
44#   genFuncPointers - True if function pointer typedefs should be
45#     generated
46#   protectProto - If conditional protection should be generated
47#     around prototype declarations, set to either '#ifdef'
48#     to require opt-in (#ifdef protectProtoStr) or '#ifndef'
49#     to require opt-out (#ifndef protectProtoStr). Otherwise
50#     set to None.
51#   protectProtoStr - #ifdef/#ifndef symbol to use around prototype
52#     declarations, if protectProto is set
53#   apicall - string to use for the function declaration prefix,
54#     such as APICALL on Windows.
55#   apientry - string to use for the calling convention macro,
56#     in typedefs, such as APIENTRY.
57#   apientryp - string to use for the calling convention macro
58#     in function pointer typedefs, such as APIENTRYP.
59#   indentFuncProto - True if prototype declarations should put each
60#     parameter on a separate line
61#   indentFuncPointer - True if typedefed function pointers should put each
62#     parameter on a separate line
63#   alignFuncParam - if nonzero and parameters are being put on a
64#     separate line, align parameter names at the specified column
65class ParameterValidationGeneratorOptions(GeneratorOptions):
66    def __init__(self,
67                 filename = None,
68                 directory = '.',
69                 apiname = None,
70                 profile = None,
71                 versions = '.*',
72                 emitversions = '.*',
73                 defaultExtensions = None,
74                 addExtensions = None,
75                 removeExtensions = None,
76                 emitExtensions = None,
77                 sortProcedure = regSortFeatures,
78                 prefixText = "",
79                 apicall = '',
80                 apientry = '',
81                 apientryp = '',
82                 indentFuncProto = True,
83                 indentFuncPointer = False,
84                 alignFuncParam = 0,
85                 expandEnumerants = True,
86                 valid_usage_path = ''):
87        GeneratorOptions.__init__(self, filename, directory, apiname, profile,
88                                  versions, emitversions, defaultExtensions,
89                                  addExtensions, removeExtensions, emitExtensions, sortProcedure)
90        self.prefixText      = prefixText
91        self.apicall         = apicall
92        self.apientry        = apientry
93        self.apientryp       = apientryp
94        self.indentFuncProto = indentFuncProto
95        self.indentFuncPointer = indentFuncPointer
96        self.alignFuncParam  = alignFuncParam
97        self.expandEnumerants = expandEnumerants
98        self.valid_usage_path = valid_usage_path
99
100# ParameterValidationOutputGenerator - subclass of OutputGenerator.
101# Generates param checker layer code.
102#
103# ---- methods ----
104# ParamCheckerOutputGenerator(errFile, warnFile, diagFile) - args as for
105#   OutputGenerator. Defines additional internal state.
106# ---- methods overriding base class ----
107# beginFile(genOpts)
108# endFile()
109# beginFeature(interface, emit)
110# endFeature()
111# genType(typeinfo,name)
112# genStruct(typeinfo,name)
113# genGroup(groupinfo,name)
114# genEnum(enuminfo, name)
115# genCmd(cmdinfo)
116class ParameterValidationOutputGenerator(OutputGenerator):
117    """Generate Parameter Validation code based on XML element attributes"""
118    # This is an ordered list of sections in the header file.
119    ALL_SECTIONS = ['command']
120    def __init__(self,
121                 errFile = sys.stderr,
122                 warnFile = sys.stderr,
123                 diagFile = sys.stdout):
124        OutputGenerator.__init__(self, errFile, warnFile, diagFile)
125        self.INDENT_SPACES = 4
126        self.declarations = []
127
128        inline_custom_source_preamble = """
129"""
130
131        # These functions have additional, custom-written checks in the utils cpp file. CodeGen will automatically add a call
132        # to those functions of the form 'bool manual_PreCallValidateAPIName', where the 'vk' is dropped.
133        # see 'manual_PreCallValidateCreateGraphicsPipelines' as an example.
134        self.functions_with_manual_checks = [
135            'vkCreateInstance',
136            'vkCreateDevice',
137            'vkCreateQueryPool'
138            'vkCreateRenderPass',
139            'vkCreateRenderPass2KHR',
140            'vkCreateBuffer',
141            'vkCreateImage',
142            'vkCreateImageView',
143            'vkCreateGraphicsPipelines',
144            'vkCreateComputePipelines',
145            'vkCreateSampler',
146            'vkCreateDescriptorSetLayout',
147            'vkFreeDescriptorSets',
148            'vkUpdateDescriptorSets',
149            'vkCreateRenderPass',
150            'vkCreateRenderPass2KHR',
151            'vkBeginCommandBuffer',
152            'vkCmdSetViewport',
153            'vkCmdSetScissor',
154            'vkCmdSetLineWidth',
155            'vkCmdDraw',
156            'vkCmdDrawIndirect',
157            'vkCmdDrawIndexedIndirect',
158            'vkCmdCopyImage',
159            'vkCmdBlitImage',
160            'vkCmdCopyBufferToImage',
161            'vkCmdCopyImageToBuffer',
162            'vkCmdUpdateBuffer',
163            'vkCmdFillBuffer',
164            'vkCreateSwapchainKHR',
165            'vkQueuePresentKHR',
166            'vkCreateDescriptorPool',
167            'vkCmdDispatch',
168            'vkCmdDispatchIndirect',
169            'vkCmdDispatchBaseKHR',
170            'vkCmdSetExclusiveScissorNV',
171            'vkCmdSetViewportShadingRatePaletteNV',
172            'vkCmdSetCoarseSampleOrderNV',
173            'vkCmdDrawMeshTasksNV',
174            'vkCmdDrawMeshTasksIndirectNV',
175            'vkCmdDrawMeshTasksIndirectCountNV',
176            'vkAllocateMemory',
177            ]
178
179        # Commands to ignore
180        self.blacklist = [
181            'vkGetInstanceProcAddr',
182            'vkGetDeviceProcAddr',
183            'vkEnumerateInstanceVersion',
184            'vkEnumerateInstanceLayerProperties',
185            'vkEnumerateInstanceExtensionProperties',
186            'vkEnumerateDeviceLayerProperties',
187            'vkEnumerateDeviceExtensionProperties',
188            ]
189
190        # Structure fields to ignore
191        self.structMemberBlacklist = { 'VkWriteDescriptorSet' : ['dstSet'] }
192        # Validation conditions for some special case struct members that are conditionally validated
193        self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
194        # Header version
195        self.headerVersion = None
196        # Internal state - accumulators for different inner block text
197        self.validation = []                              # Text comprising the main per-api parameter validation routines
198        self.stypes = []                                  # Values from the VkStructureType enumeration
199        self.structTypes = dict()                         # Map of Vulkan struct typename to required VkStructureType
200        self.handleTypes = set()                          # Set of handle type names
201        self.commands = []                                # List of CommandData records for all Vulkan commands
202        self.structMembers = []                           # List of StructMemberData records for all Vulkan structs
203        self.validatedStructs = dict()                    # Map of structs type names to generated validation code for that struct type
204        self.enumRanges = dict()                          # Map of enum name to BEGIN/END range values
205        self.enumValueLists = ''                          # String containing enumerated type map definitions
206        self.flags = set()                                # Map of flags typenames
207        self.flagBits = dict()                            # Map of flag bits typename to list of values
208        self.newFlags = set()                             # Map of flags typenames /defined in the current feature/
209        self.required_extensions = dict()                 # Dictionary of required extensions for each item in the current extension
210        self.extension_type = ''                          # Type of active feature (extension), device or instance
211        self.extension_names = dict()                     # Dictionary of extension names to extension name defines
212        self.structextends_list = []                      # List of extensions which extend another struct
213        self.struct_feature_protect = dict()              # Dictionary of structnames and FeatureExtraProtect strings
214        self.valid_vuids = set()                          # Set of all valid VUIDs
215        self.vuid_dict = dict()                           # VUID dictionary (from JSON)
216        self.alias_dict = dict()                          # Dict of cmd|struct aliases
217        self.header_file = False                          # Header file generation flag
218        self.source_file = False                          # Source file generation flag
219        self.returnedonly_structs = []
220        # Named tuples to store struct and command data
221        self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
222                                                        'isconst', 'isoptional', 'iscount', 'noautovalidity',
223                                                        'len', 'extstructs', 'condition', 'cdecl'])
224        self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl', 'extension_type', 'result'])
225        self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
226
227    #
228    # Generate Copyright comment block for file
229    def GenerateCopyright(self):
230        copyright  = '/* *** THIS FILE IS GENERATED - DO NOT EDIT! ***\n'
231        copyright += ' * See parameter_validation_generator.py for modifications\n'
232        copyright += ' *\n'
233        copyright += ' * Copyright (c) 2015-2018 The Khronos Group Inc.\n'
234        copyright += ' * Copyright (c) 2015-2018 LunarG, Inc.\n'
235        copyright += ' * Copyright (C) 2015-2018 Google Inc.\n'
236        copyright += ' *\n'
237        copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
238        copyright += ' * you may not use this file except in compliance with the License.\n'
239        copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n'
240        copyright += ' * You may obtain a copy of the License at\n'
241        copyright += ' *\n'
242        copyright += ' *     http://www.apache.org/licenses/LICENSE-2.0\n'
243        copyright += ' *\n'
244        copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
245        copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
246        copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
247        copyright += ' * See the License for the specific language governing permissions and\n'
248        copyright += ' * limitations under the License.\n'
249        copyright += ' *\n'
250        copyright += ' * Author: Mark Lobodzinski <mark@LunarG.com>\n'
251        copyright += ' * Author: Dave Houlton <daveh@LunarG.com>\n'
252        copyright += ' */\n\n'
253        return copyright
254    #
255    # Increases the global indent variable
256    def incIndent(self, indent):
257        inc = ' ' * self.INDENT_SPACES
258        if indent:
259            return indent + inc
260        return inc
261    #
262    # Decreases the global indent variable
263    def decIndent(self, indent):
264        if indent and (len(indent) > self.INDENT_SPACES):
265            return indent[:-self.INDENT_SPACES]
266        return ''
267    #
268    # Walk the JSON-derived dict and find all "vuid" key values
269    def ExtractVUIDs(self, d):
270        if hasattr(d, 'items'):
271            for k, v in d.items():
272                if k == "vuid":
273                    yield v
274                elif isinstance(v, dict):
275                    for s in self.ExtractVUIDs(v):
276                        yield s
277                elif isinstance (v, list):
278                    for l in v:
279                        for s in self.ExtractVUIDs(l):
280                            yield s
281    #
282    # Called at file creation time
283    def beginFile(self, genOpts):
284        OutputGenerator.beginFile(self, genOpts)
285        self.header_file = (genOpts.filename == 'parameter_validation.h')
286        self.source_file = (genOpts.filename == 'parameter_validation.cpp')
287
288        if not self.header_file and not self.source_file:
289            print("Error: Output Filenames have changed, update generator source.\n")
290            sys.exit(1)
291
292        if self.source_file or self.header_file:
293            # Output Copyright text
294            s = self.GenerateCopyright()
295            write(s, file=self.outFile)
296
297        if self.header_file:
298            return
299
300        # Build map of structure type names to VkStructureType enum values
301        # Find all types of category "struct"
302        for struct in self.registry.tree.iterfind('types/type[@category="struct"]'):
303            # Check if struct has member named "sType" of type "VkStructureType" which has values defined
304            stype = struct.find('member[name="sType"][type="VkStructureType"][@values]')
305            if stype is not None:
306                # Store VkStructureType value for this type
307                self.structTypes[struct.get('name')] = stype.get('values')
308
309        self.valid_usage_path = genOpts.valid_usage_path
310        vu_json_filename = os.path.join(self.valid_usage_path + os.sep, 'validusage.json')
311        if os.path.isfile(vu_json_filename):
312            json_file = open(vu_json_filename, 'r')
313            self.vuid_dict = json.load(json_file)
314            json_file.close()
315        if len(self.vuid_dict) == 0:
316            print("Error: Could not find, or error loading %s/validusage.json\n", vu_json_filename)
317            sys.exit(1)
318        #
319        # Build a set of all vuid text strings found in validusage.json
320        for json_vuid_string in self.ExtractVUIDs(self.vuid_dict):
321            self.valid_vuids.add(json_vuid_string)
322        #
323        # Headers
324        write('#include "chassis.h"', file=self.outFile)
325        self.newline()
326        write('#include "stateless_validation.h"', file=self.outFile)
327        self.newline()
328    #
329    # Called at end-time for final content output
330    def endFile(self):
331        if self.source_file:
332            # C-specific
333            self.newline()
334            write(self.enumValueLists, file=self.outFile)
335            self.newline()
336
337            pnext_handler  = 'bool StatelessValidation::ValidatePnextStructContents(const char *api_name, const ParameterName &parameter_name, const GenericHeader* header) {\n'
338            pnext_handler += '    bool skip = false;\n'
339            pnext_handler += '    switch(header->sType) {\n'
340
341            # Do some processing here to extract data from validatedstructs...
342            for item in self.structextends_list:
343                postProcSpec = {}
344                postProcSpec['ppp'] = '' if not item else '{postProcPrefix}'
345                postProcSpec['pps'] = '' if not item else '{postProcSuffix}'
346                postProcSpec['ppi'] = '' if not item else '{postProcInsert}'
347
348                pnext_case = '\n'
349                protect = ''
350                # Guard struct cases with feature ifdefs, if necessary
351                if item in self.struct_feature_protect.keys():
352                    protect = self.struct_feature_protect[item]
353                    pnext_case += '#ifdef %s\n' % protect
354                pnext_case += '        // Validation code for %s structure members\n' % item
355                pnext_case += '        case %s: {\n' % self.structTypes[item]
356                pnext_case += '            %s *structure = (%s *) header;\n' % (item, item)
357                expr = self.expandStructCode(item, item, 'structure->', '', '            ', [], postProcSpec)
358                struct_validation_source = self.ScrubStructCode(expr)
359                pnext_case += '%s' % struct_validation_source
360                pnext_case += '        } break;\n'
361                if protect is not '':
362                    pnext_case += '#endif // %s\n' % protect
363                # Skip functions containing no validation
364                if struct_validation_source != '':
365                    pnext_handler += pnext_case;
366            pnext_handler += '        default:\n'
367            pnext_handler += '            skip = false;\n'
368            pnext_handler += '    }\n'
369            pnext_handler += '    return skip;\n'
370            pnext_handler += '}\n'
371            write(pnext_handler, file=self.outFile)
372            self.newline()
373
374            ext_template  = 'bool StatelessValidation::OutputExtensionError(const std::string &api_name, const std::string &extension_name) {\n'
375            ext_template += '    return log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,\n'
376            ext_template += '                   kVUID_PVError_ExtensionNotEnabled, "Attemped to call %s() but its required extension %s has not been enabled\\n",\n'
377            ext_template += '                   api_name.c_str(), extension_name.c_str());\n'
378            ext_template += '}\n'
379            write(ext_template, file=self.outFile)
380            self.newline()
381            commands_text = '\n'.join(self.validation)
382            write(commands_text, file=self.outFile)
383            self.newline()
384        if self.header_file:
385            # Output declarations and record intercepted procedures
386            write('\n'.join(self.declarations), file=self.outFile)
387            # Finish processing in superclass
388            OutputGenerator.endFile(self)
389    #
390    # Processing at beginning of each feature or extension
391    def beginFeature(self, interface, emit):
392        # Start processing in superclass
393        OutputGenerator.beginFeature(self, interface, emit)
394        # C-specific
395        # Accumulate includes, defines, types, enums, function pointer typedefs, end function prototypes separately for this
396        # feature. They're only printed in endFeature().
397        self.headerVersion = None
398        self.stypes = []
399        self.commands = []
400        self.structMembers = []
401        self.newFlags = set()
402        self.featureExtraProtect = GetFeatureProtect(interface)
403        # Get base list of extension dependencies for all items in this extension
404        base_required_extensions = []
405        if "VK_VERSION_1" not in self.featureName:
406            # Save Name Define to get correct enable name later
407            nameElem = interface[0][1]
408            name = nameElem.get('name')
409            self.extension_names[self.featureName] = name
410            # This extension is the first dependency for this command
411            base_required_extensions.append(self.featureName)
412        # Add any defined extension dependencies to the base dependency list for this extension
413        requires = interface.get('requires')
414        if requires is not None:
415            base_required_extensions.extend(requires.split(','))
416        # Build dictionary of extension dependencies for each item in this extension
417        self.required_extensions = dict()
418        for require_element in interface.findall('require'):
419            # Copy base extension dependency list
420            required_extensions = list(base_required_extensions)
421            # Add any additional extension dependencies specified in this require block
422            additional_extensions = require_element.get('extension')
423            if additional_extensions:
424                required_extensions.extend(additional_extensions.split(','))
425            # Save full extension list for all named items
426            for element in require_element.findall('*[@name]'):
427                self.required_extensions[element.get('name')] = required_extensions
428
429        # And note if this is an Instance or Device extension
430        self.extension_type = interface.get('type')
431    #
432    # Called at the end of each extension (feature)
433    def endFeature(self):
434        if self.header_file:
435            return
436        # C-specific
437        # Actually write the interface to the output file.
438        if (self.emit):
439            # If type declarations are needed by other features based on this one, it may be necessary to suppress the ExtraProtect,
440            # or move it below the 'for section...' loop.
441            ifdef = ''
442            if (self.featureExtraProtect is not None):
443                ifdef = '#ifdef %s\n' % self.featureExtraProtect
444                self.validation.append(ifdef)
445            # Generate the struct member checking code from the captured data
446            self.processStructMemberData()
447            # Generate the command parameter checking code from the captured data
448            self.processCmdData()
449            # Write the declaration for the HeaderVersion
450            if self.headerVersion:
451                write('const uint32_t GeneratedVulkanHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
452                self.newline()
453            # Write the declarations for the VkFlags values combining all flag bits
454            for flag in sorted(self.newFlags):
455                flagBits = flag.replace('Flags', 'FlagBits')
456                if flagBits in self.flagBits:
457                    bits = self.flagBits[flagBits]
458                    decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
459                    for bit in bits[1:]:
460                        decl += '|' + bit
461                    decl += ';'
462                    write(decl, file=self.outFile)
463            endif = '\n'
464            if (self.featureExtraProtect is not None):
465                endif = '#endif // %s\n' % self.featureExtraProtect
466            self.validation.append(endif)
467        # Finish processing in superclass
468        OutputGenerator.endFeature(self)
469    #
470    # Type generation
471    def genType(self, typeinfo, name, alias):
472        # record the name/alias pair
473        if alias is not None:
474            self.alias_dict[name]=alias
475        OutputGenerator.genType(self, typeinfo, name, alias)
476        typeElem = typeinfo.elem
477        # If the type is a struct type, traverse the embedded <member> tags generating a structure. Otherwise, emit the tag text.
478        category = typeElem.get('category')
479        if (category == 'struct' or category == 'union'):
480            self.genStruct(typeinfo, name, alias)
481        elif (category == 'handle'):
482            self.handleTypes.add(name)
483        elif (category == 'bitmask'):
484            self.flags.add(name)
485            self.newFlags.add(name)
486        elif (category == 'define'):
487            if name == 'VK_HEADER_VERSION':
488                nameElem = typeElem.find('name')
489                self.headerVersion = noneStr(nameElem.tail).strip()
490    #
491    # Struct parameter check generation.
492    # This is a special case of the <type> tag where the contents are interpreted as a set of <member> tags instead of freeform C
493    # type declarations. The <member> tags are just like <param> tags - they are a declaration of a struct or union member.
494    # Only simple member declarations are supported (no nested structs etc.)
495    def genStruct(self, typeinfo, typeName, alias):
496        if not self.source_file:
497            return
498        # alias has already been recorded in genType, above
499        OutputGenerator.genStruct(self, typeinfo, typeName, alias)
500        conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
501        members = typeinfo.elem.findall('.//member')
502        if self.featureExtraProtect is not None:
503            self.struct_feature_protect[typeName] = self.featureExtraProtect
504        #
505        # Iterate over members once to get length parameters for arrays
506        lens = set()
507        for member in members:
508            len = self.getLen(member)
509            if len:
510                lens.add(len)
511        #
512        # Generate member info
513        membersInfo = []
514        for member in members:
515            # Get the member's type and name
516            info = self.getTypeNameTuple(member)
517            type = info[0]
518            name = info[1]
519            stypeValue = ''
520            cdecl = self.makeCParamDecl(member, 0)
521
522            # Store pointer/array/string info -- Check for parameter name in lens set
523            iscount = False
524            if name in lens:
525                iscount = True
526            # The pNext members are not tagged as optional, but are treated as optional for parameter NULL checks.  Static array
527            # members are also treated as optional to skip NULL pointer validation, as they won't be NULL.
528            isstaticarray = self.paramIsStaticArray(member)
529            isoptional = False
530            if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
531                isoptional = True
532            # Determine if value should be ignored by code generation.
533            noautovalidity = False
534            if (member.attrib.get('noautovalidity') is not None) or ((typeName in self.structMemberBlacklist) and (name in self.structMemberBlacklist[typeName])):
535                noautovalidity = True
536            structextends = False
537            membersInfo.append(self.CommandParam(type=type, name=name,
538                                                ispointer=self.paramIsPointer(member),
539                                                isstaticarray=isstaticarray,
540                                                isbool=True if type == 'VkBool32' else False,
541                                                israngedenum=True if type in self.enumRanges else False,
542                                                isconst=True if 'const' in cdecl else False,
543                                                isoptional=isoptional,
544                                                iscount=iscount,
545                                                noautovalidity=noautovalidity,
546                                                len=self.getLen(member),
547                                                extstructs=self.registry.validextensionstructs[typeName] if name == 'pNext' else None,
548                                                condition=conditions[name] if conditions and name in conditions else None,
549                                                cdecl=cdecl))
550        # If this struct extends another, keep its name in list for further processing
551        if typeinfo.elem.attrib.get('structextends') is not None:
552            self.structextends_list.append(typeName)
553        # Returnedonly structs should have most of their members ignored -- on entry, we only care about validating the sType and
554        # pNext members. Everything else will be overwritten by the callee.
555        if typeinfo.elem.attrib.get('returnedonly') is not None:
556            self.returnedonly_structs.append(typeName)
557            membersInfo = [m for m in membersInfo if m.name in ('sType', 'pNext')]
558        self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
559    #
560    # Capture group (e.g. C "enum" type) info to be used for param check code generation.
561    # These are concatenated together with other types.
562    def genGroup(self, groupinfo, groupName, alias):
563        if not self.source_file:
564            return
565        # record the name/alias pair
566        if alias is not None:
567            self.alias_dict[groupName]=alias
568        OutputGenerator.genGroup(self, groupinfo, groupName, alias)
569        groupElem = groupinfo.elem
570        # Store the sType values
571        if groupName == 'VkStructureType':
572            for elem in groupElem.findall('enum'):
573                self.stypes.append(elem.get('name'))
574        elif 'FlagBits' in groupName:
575            bits = []
576            for elem in groupElem.findall('enum'):
577                if elem.get('supported') != 'disabled':
578                    bits.append(elem.get('name'))
579            if bits:
580                self.flagBits[groupName] = bits
581        else:
582            # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
583            expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
584            expandPrefix = expandName
585            expandSuffix = ''
586            expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
587            if expandSuffixMatch:
588                expandSuffix = '_' + expandSuffixMatch.group()
589                # Strip off the suffix from the prefix
590                expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
591            isEnum = ('FLAG_BITS' not in expandPrefix)
592            if isEnum:
593                self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
594                # Create definition for a list containing valid enum values for this enumerated type
595                enum_entry = 'const std::vector<%s> All%sEnums = {' % (groupName, groupName)
596                for enum in groupElem:
597                    name = enum.get('name')
598                    if name is not None and enum.get('supported') != 'disabled':
599                        enum_entry += '%s, ' % name
600                enum_entry += '};\n'
601                self.enumValueLists += enum_entry
602    #
603    # Capture command parameter info to be used for param check code generation.
604    def genCmd(self, cmdinfo, name, alias):
605        # record the name/alias pair
606        if alias is not None:
607            self.alias_dict[name]=alias
608        OutputGenerator.genCmd(self, cmdinfo, name, alias)
609        decls = self.makeCDecls(cmdinfo.elem)
610        typedef = decls[1]
611        typedef = typedef.split(')',1)[1]
612        if self.header_file:
613            if name not in self.blacklist:
614                if (self.featureExtraProtect is not None):
615                    self.declarations += [ '#ifdef %s' % self.featureExtraProtect ]
616                # Strip off 'vk' from API name
617                self.declarations += [ '%s%s' % ('bool PreCallValidate', decls[0].split("VKAPI_CALL vk")[1])]
618                if (self.featureExtraProtect is not None):
619                    self.declarations += [ '#endif' ]
620        if self.source_file:
621            if name not in self.blacklist:
622                params = cmdinfo.elem.findall('param')
623                # Get list of array lengths
624                lens = set()
625                for param in params:
626                    len = self.getLen(param)
627                    if len:
628                        lens.add(len)
629                # Get param info
630                paramsInfo = []
631                for param in params:
632                    paramInfo = self.getTypeNameTuple(param)
633                    cdecl = self.makeCParamDecl(param, 0)
634                    # Check for parameter name in lens set
635                    iscount = False
636                    if paramInfo[1] in lens:
637                        iscount = True
638                    paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
639                                                        ispointer=self.paramIsPointer(param),
640                                                        isstaticarray=self.paramIsStaticArray(param),
641                                                        isbool=True if paramInfo[0] == 'VkBool32' else False,
642                                                        israngedenum=True if paramInfo[0] in self.enumRanges else False,
643                                                        isconst=True if 'const' in cdecl else False,
644                                                        isoptional=self.paramIsOptional(param),
645                                                        iscount=iscount,
646                                                        noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
647                                                        len=self.getLen(param),
648                                                        extstructs=None,
649                                                        condition=None,
650                                                        cdecl=cdecl))
651                # Save return value information, if any
652                result_type = ''
653                resultinfo = cmdinfo.elem.find('proto/type')
654                if (resultinfo is not None and resultinfo.text != 'void'):
655                    result_type = resultinfo.text
656                self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0], extension_type=self.extension_type, result=result_type))
657    #
658    # Check if the parameter passed in is a pointer
659    def paramIsPointer(self, param):
660        ispointer = 0
661        paramtype = param.find('type')
662        if (paramtype.tail is not None) and ('*' in paramtype.tail):
663            ispointer = paramtype.tail.count('*')
664        elif paramtype.text[:4] == 'PFN_':
665            # Treat function pointer typedefs as a pointer to a single value
666            ispointer = 1
667        return ispointer
668    #
669    # Check if the parameter passed in is a static array
670    def paramIsStaticArray(self, param):
671        isstaticarray = 0
672        paramname = param.find('name')
673        if (paramname.tail is not None) and ('[' in paramname.tail):
674            isstaticarray = paramname.tail.count('[')
675        return isstaticarray
676    #
677    # Check if the parameter passed in is optional
678    # Returns a list of Boolean values for comma separated len attributes (len='false,true')
679    def paramIsOptional(self, param):
680        # See if the handle is optional
681        isoptional = False
682        # Simple, if it's optional, return true
683        optString = param.attrib.get('optional')
684        if optString:
685            if optString == 'true':
686                isoptional = True
687            elif ',' in optString:
688                opts = []
689                for opt in optString.split(','):
690                    val = opt.strip()
691                    if val == 'true':
692                        opts.append(True)
693                    elif val == 'false':
694                        opts.append(False)
695                    else:
696                        print('Unrecognized len attribute value',val)
697                isoptional = opts
698        return isoptional
699    #
700    # Check if the handle passed in is optional
701    # Uses the same logic as ValidityOutputGenerator.isHandleOptional
702    def isHandleOptional(self, param, lenParam):
703        # Simple, if it's optional, return true
704        if param.isoptional:
705            return True
706        # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
707        if param.noautovalidity:
708            return True
709        # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
710        if lenParam and lenParam.isoptional:
711            return True
712        return False
713    #
714    # Retrieve the value of the len tag
715    def getLen(self, param):
716        result = None
717        len = param.attrib.get('len')
718        if len and len != 'null-terminated':
719            # For string arrays, 'len' can look like 'count,null-terminated', indicating that we have a null terminated array of
720            # strings.  We strip the null-terminated from the 'len' field and only return the parameter specifying the string count
721            if 'null-terminated' in len:
722                result = len.split(',')[0]
723            else:
724                result = len
725            result = str(result).replace('::', '->')
726        return result
727    #
728    # Retrieve the type and name for a parameter
729    def getTypeNameTuple(self, param):
730        type = ''
731        name = ''
732        for elem in param:
733            if elem.tag == 'type':
734                type = noneStr(elem.text)
735            elif elem.tag == 'name':
736                name = noneStr(elem.text)
737        return (type, name)
738    #
739    # Find a named parameter in a parameter list
740    def getParamByName(self, params, name):
741        for param in params:
742            if param.name == name:
743                return param
744        return None
745    #
746    # Extract length values from latexmath.  Currently an inflexible solution that looks for specific
747    # patterns that are found in vk.xml.  Will need to be updated when new patterns are introduced.
748    def parseLateXMath(self, source):
749        name = 'ERROR'
750        decoratedName = 'ERROR'
751        if 'mathit' in source:
752            # Matches expressions similar to 'latexmath:[\lceil{\mathit{rasterizationSamples} \over 32}\rceil]'
753            match = re.match(r'latexmath\s*\:\s*\[\s*\\l(\w+)\s*\{\s*\\mathit\s*\{\s*(\w+)\s*\}\s*\\over\s*(\d+)\s*\}\s*\\r(\w+)\s*\]', source)
754            if not match or match.group(1) != match.group(4):
755                raise 'Unrecognized latexmath expression'
756            name = match.group(2)
757            decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
758        else:
759            # Matches expressions similar to 'latexmath : [dataSize \over 4]'
760            match = re.match(r'latexmath\s*\:\s*\[\s*(\\textrm\{)?(\w+)\}?\s*\\over\s*(\d+)\s*\]', source)
761            name = match.group(2)
762            decoratedName = '{}/{}'.format(*match.group(2, 3))
763        return name, decoratedName
764    #
765    # Get the length paramater record for the specified parameter name
766    def getLenParam(self, params, name):
767        lenParam = None
768        if name:
769            if '->' in name:
770                # The count is obtained by dereferencing a member of a struct parameter
771                lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
772                                             isstaticarray=None, isoptional=False, type=None, noautovalidity=False,
773                                             len=None, extstructs=None, condition=None, cdecl=None)
774            elif 'latexmath' in name:
775                lenName, decoratedName = self.parseLateXMath(name)
776                lenParam = self.getParamByName(params, lenName)
777            else:
778                lenParam = self.getParamByName(params, name)
779        return lenParam
780    #
781    # Convert a vulkan.h command declaration into a parameter_validation.h definition
782    def getCmdDef(self, cmd):
783        # Strip the trailing ';' and split into individual lines
784        lines = cmd.cdecl[:-1].split('\n')
785        cmd_hdr = '\n'.join(lines)
786        return cmd_hdr
787    #
788    # Generate the code to check for a NULL dereference before calling the
789    # validation function
790    def genCheckedLengthCall(self, name, exprs):
791        count = name.count('->')
792        if count:
793            checkedExpr = []
794            localIndent = ''
795            elements = name.split('->')
796            # Open the if expression blocks
797            for i in range(0, count):
798                checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
799                localIndent = self.incIndent(localIndent)
800            # Add the validation expression
801            for expr in exprs:
802                checkedExpr.append(localIndent + expr)
803            # Close the if blocks
804            for i in range(0, count):
805                localIndent = self.decIndent(localIndent)
806                checkedExpr.append(localIndent + '}\n')
807            return [checkedExpr]
808        # No if statements were required
809        return exprs
810    #
811    # Generate code to check for a specific condition before executing validation code
812    def genConditionalCall(self, prefix, condition, exprs):
813        checkedExpr = []
814        localIndent = ''
815        formattedCondition = condition.format(prefix)
816        checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
817        checkedExpr.append(localIndent + '{\n')
818        localIndent = self.incIndent(localIndent)
819        for expr in exprs:
820            checkedExpr.append(localIndent + expr)
821        localIndent = self.decIndent(localIndent)
822        checkedExpr.append(localIndent + '}\n')
823        return [checkedExpr]
824    #
825    # Get VUID identifier from implicit VUID tag
826    def GetVuid(self, name, suffix):
827        vuid_string = 'VUID-%s-%s' % (name, suffix)
828        vuid = "kVUIDUndefined"
829        if '->' in vuid_string:
830           return vuid
831        if vuid_string in self.valid_vuids:
832            vuid = "\"%s\"" % vuid_string
833        else:
834            if name in self.alias_dict:
835                alias_string = 'VUID-%s-%s' % (self.alias_dict[name], suffix)
836                if alias_string in self.valid_vuids:
837                    vuid = "\"%s\"" % alias_string
838        return vuid
839    #
840    # Generate the sType check string
841    def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
842        checkExpr = []
843        stype = self.structTypes[value.type]
844        vuid_name = struct_type_name if struct_type_name is not None else funcPrintName
845        stype_vuid = self.GetVuid(value.type, "sType-sType")
846        param_vuid = self.GetVuid(vuid_name, "%s-parameter" % value.name)
847
848        if lenValue:
849            count_required_vuid = self.GetVuid(vuid_name, "%s-arraylength" % lenValue.name)
850
851            # This is an array with a pointer to a count value
852            if lenValue.ispointer:
853                # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
854                checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {}, {});\n'.format(
855                    funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, stype_vuid, param_vuid, count_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype, pf=prefix, **postProcSpec))
856            # This is an array with an integer count value
857            else:
858                checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {});\n'.format(
859                    funcPrintName, lenValueRequired, valueRequired, stype_vuid, param_vuid, count_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype, pf=prefix, **postProcSpec))
860        # This is an individual struct
861        else:
862            checkExpr.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {}, {}, {});\n'.format(
863                funcPrintName, valuePrintName, prefix, valueRequired, param_vuid, stype_vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
864        return checkExpr
865    #
866    # Generate the handle check string
867    def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
868        checkExpr = []
869        if lenValue:
870            if lenValue.ispointer:
871                # This is assumed to be an output array with a pointer to a count value
872                raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
873            else:
874                # This is an array with an integer count value
875                checkExpr.append('skip |= validate_handle_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
876                    funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
877        else:
878            # This is assumed to be an output handle pointer
879            raise('Unsupported parameter validation case: Output handles are not NULL checked')
880        return checkExpr
881    #
882    # Generate check string for an array of VkFlags values
883    def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
884        checkExpr = []
885        flagBitsName = value.type.replace('Flags', 'FlagBits')
886        if not flagBitsName in self.flagBits:
887            raise('Unsupported parameter validation case: array of reserved VkFlags')
888        else:
889            allFlags = 'All' + flagBitsName
890            checkExpr.append('skip |= validate_flags_array("{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcPrintName, lenPrintName, valuePrintName, flagBitsName, allFlags, lenValue.name, value.name, lenValueRequired, valueRequired, pf=prefix, **postProcSpec))
891        return checkExpr
892    #
893    # Generate pNext check string
894    def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec, struct_type_name):
895        checkExpr = []
896        # Generate an array of acceptable VkStructureType values for pNext
897        extStructCount = 0
898        extStructVar = 'NULL'
899        extStructNames = 'NULL'
900        vuid = self.GetVuid(struct_type_name, "pNext-pNext")
901        if value.extstructs:
902            extStructVar = 'allowed_structs_{}'.format(struct_type_name)
903            extStructCount = 'ARRAY_SIZE({})'.format(extStructVar)
904            extStructNames = '"' + ', '.join(value.extstructs) + '"'
905            checkExpr.append('const VkStructureType {}[] = {{ {} }};\n'.format(extStructVar, ', '.join([self.structTypes[s] for s in value.extstructs])))
906        checkExpr.append('skip |= validate_struct_pnext("{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedVulkanHeaderVersion, {});\n'.format(
907            funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, vuid, **postProcSpec))
908        return checkExpr
909    #
910    # Generate the pointer check string
911    def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
912        checkExpr = []
913        vuid_tag_name = struct_type_name if struct_type_name is not None else funcPrintName
914        if lenValue:
915            count_required_vuid = self.GetVuid(vuid_tag_name, "%s-arraylength" % (lenValue.name))
916            array_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
917            # This is an array with a pointer to a count value
918            if lenValue.ispointer:
919                # If count and array parameters are optional, there will be no validation
920                if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
921                    # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
922                    checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {}, {});\n'.format(
923                        funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
924            # This is an array with an integer count value
925            else:
926                # If count and array parameters are optional, there will be no validation
927                if valueRequired == 'true' or lenValueRequired == 'true':
928                    if value.type != 'char':
929                        checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {});\n'.format(
930                            funcPrintName, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
931                    else:
932                        # Arrays of strings receive special processing
933                        checkExpr.append('skip |= validate_string_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {}, {});\n'.format(
934                            funcPrintName, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
935            if checkExpr:
936                if lenValue and ('->' in lenValue.name):
937                    # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
938                    checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
939        # This is an individual struct that is not allowed to be NULL
940        elif not value.isoptional:
941            # Function pointers need a reinterpret_cast to void*
942            ptr_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
943            if value.type[:4] == 'PFN_':
944                allocator_dict = {'pfnAllocation': '"VUID-VkAllocationCallbacks-pfnAllocation-00632"',
945                                  'pfnReallocation': '"VUID-VkAllocationCallbacks-pfnReallocation-00633"',
946                                  'pfnFree': '"VUID-VkAllocationCallbacks-pfnFree-00634"',
947                                  'pfnInternalAllocation': '"VUID-VkAllocationCallbacks-pfnInternalAllocation-00635"'
948                                 }
949                vuid = allocator_dict.get(value.name)
950                if vuid is not None:
951                    ptr_required_vuid = vuid
952                checkExpr.append('skip |= validate_required_pointer("{}", {ppp}"{}"{pps}, reinterpret_cast<const void*>({}{}), {});\n'.format(funcPrintName, valuePrintName, prefix, value.name, ptr_required_vuid, **postProcSpec))
953            else:
954                checkExpr.append('skip |= validate_required_pointer("{}", {ppp}"{}"{pps}, {}{}, {});\n'.format(funcPrintName, valuePrintName, prefix, value.name, ptr_required_vuid, **postProcSpec))
955        return checkExpr
956    #
957    # Process struct member validation code, performing name substitution if required
958    def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
959        # Build format specifier list
960        kwargs = {}
961        if '{postProcPrefix}' in line:
962            # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
963            if type(memberDisplayNamePrefix) is tuple:
964                kwargs['postProcPrefix'] = 'ParameterName('
965            else:
966                kwargs['postProcPrefix'] = postProcSpec['ppp']
967        if '{postProcSuffix}' in line:
968            # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
969            if type(memberDisplayNamePrefix) is tuple:
970                kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
971            else:
972                kwargs['postProcSuffix'] = postProcSpec['pps']
973        if '{postProcInsert}' in line:
974            # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
975            if type(memberDisplayNamePrefix) is tuple:
976                kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
977            else:
978                kwargs['postProcInsert'] = postProcSpec['ppi']
979        if '{funcName}' in line:
980            kwargs['funcName'] = funcName
981        if '{valuePrefix}' in line:
982            kwargs['valuePrefix'] = memberNamePrefix
983        if '{displayNamePrefix}' in line:
984            # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
985            if type(memberDisplayNamePrefix) is tuple:
986                kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
987            else:
988                kwargs['displayNamePrefix'] = memberDisplayNamePrefix
989
990        if kwargs:
991            # Need to escape the C++ curly braces
992            if 'IndexVector' in line:
993                line = line.replace('IndexVector{ ', 'IndexVector{{ ')
994                line = line.replace(' }),', ' }}),')
995            return line.format(**kwargs)
996        return line
997    #
998    # Process struct member validation code, stripping metadata
999    def ScrubStructCode(self, code):
1000        scrubbed_lines = ''
1001        for line in code:
1002            if 'validate_struct_pnext' in line:
1003                continue
1004            if 'allowed_structs' in line:
1005                continue
1006            if 'xml-driven validation' in line:
1007                continue
1008            line = line.replace('{postProcPrefix}', '')
1009            line = line.replace('{postProcSuffix}', '')
1010            line = line.replace('{postProcInsert}', '')
1011            line = line.replace('{funcName}', '')
1012            line = line.replace('{valuePrefix}', '')
1013            line = line.replace('{displayNamePrefix}', '')
1014            line = line.replace('{IndexVector}', '')
1015            line = line.replace('local_data->', '')
1016            scrubbed_lines += line
1017        return scrubbed_lines
1018    #
1019    # Process struct validation code for inclusion in function or parent struct validation code
1020    def expandStructCode(self, item_type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
1021        lines = self.validatedStructs[item_type]
1022        for line in lines:
1023            if output:
1024                output[-1] += '\n'
1025            if type(line) is list:
1026                for sub in line:
1027                    output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1028            else:
1029                output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1030        return output
1031    #
1032    # Process struct pointer/array validation code, performing name substitution if required
1033    def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
1034        expr = []
1035        expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
1036        expr.append('{')
1037        indent = self.incIndent(None)
1038        if lenValue:
1039            # Need to process all elements in the array
1040            indexName = lenValue.name.replace('Count', 'Index')
1041            expr[-1] += '\n'
1042            if lenValue.ispointer:
1043                # If the length value is a pointer, de-reference it for the count.
1044                expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < *{}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
1045            else:
1046                expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
1047            expr.append(indent + '{')
1048            indent = self.incIndent(indent)
1049            # Prefix for value name to display in error message
1050            if value.ispointer == 2:
1051                memberNamePrefix = '{}{}[{}]->'.format(prefix, value.name, indexName)
1052                memberDisplayNamePrefix = ('{}[%i]->'.format(valueDisplayName), indexName)
1053            else:
1054                memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
1055                memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
1056        else:
1057            memberNamePrefix = '{}{}->'.format(prefix, value.name)
1058            memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
1059        # Expand the struct validation lines
1060        expr = self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
1061        if lenValue:
1062            # Close if and for scopes
1063            indent = self.decIndent(indent)
1064            expr.append(indent + '}\n')
1065        expr.append('}\n')
1066        return expr
1067    #
1068    # Generate the parameter checking code
1069    def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
1070        lines = []    # Generated lines of code
1071        unused = []   # Unused variable names
1072        for value in values:
1073            usedLines = []
1074            lenParam = None
1075            #
1076            # Prefix and suffix for post processing of parameter names for struct members.  Arrays of structures need special processing to include the array index in the full parameter name.
1077            postProcSpec = {}
1078            postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
1079            postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
1080            postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
1081            #
1082            # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name
1083            valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
1084            #
1085            # Check for NULL pointers, ignore the in-out count parameters that
1086            # will be validated with their associated array
1087            if (value.ispointer or value.isstaticarray) and not value.iscount:
1088                # Parameters for function argument generation
1089                req = 'true'    # Parameter cannot be NULL
1090                cpReq = 'true'  # Count pointer cannot be NULL
1091                cvReq = 'true'  # Count value cannot be 0
1092                lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
1093                # Generate required/optional parameter strings for the pointer and count values
1094                if value.isoptional:
1095                    req = 'false'
1096                if value.len:
1097                    # The parameter is an array with an explicit count parameter
1098                    lenParam = self.getLenParam(values, value.len)
1099                    lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
1100                    if lenParam.ispointer:
1101                        # Count parameters that are pointers are inout
1102                        if type(lenParam.isoptional) is list:
1103                            if lenParam.isoptional[0]:
1104                                cpReq = 'false'
1105                            if lenParam.isoptional[1]:
1106                                cvReq = 'false'
1107                        else:
1108                            if lenParam.isoptional:
1109                                cpReq = 'false'
1110                    else:
1111                        if lenParam.isoptional:
1112                            cvReq = 'false'
1113                #
1114                # The parameter will not be processed when tagged as 'noautovalidity'
1115                # For the pointer to struct case, the struct pointer will not be validated, but any
1116                # members not tagged as 'noautovalidity' will be validated
1117                # We special-case the custom allocator checks, as they are explicit but can be auto-generated.
1118                AllocatorFunctions = ['PFN_vkAllocationFunction', 'PFN_vkReallocationFunction', 'PFN_vkFreeFunction']
1119                if value.noautovalidity and value.type not in AllocatorFunctions:
1120                    # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1121                    self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1122                else:
1123                    if value.type in self.structTypes:
1124                        # If this is a pointer to a struct with an sType field, verify the type
1125                        usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
1126                    # If this is an input handle array that is not allowed to contain NULL handles, verify that none of the handles are VK_NULL_HANDLE
1127                    elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
1128                        usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1129                    elif value.type in self.flags and value.isconst:
1130                        usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1131                    elif value.isbool and value.isconst:
1132                        usedLines.append('skip |= validate_bool32_array("{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
1133                    elif value.israngedenum and value.isconst:
1134                        enum_value_list = 'All%sEnums' % value.type
1135                        usedLines.append('skip |= validate_ranged_enum_array("{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, value.type, enum_value_list, lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
1136                    elif value.name == 'pNext' and value.isconst:
1137                        usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec, structTypeName)
1138                    else:
1139                        usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
1140                    # If this is a pointer to a struct (input), see if it contains members that need to be checked
1141                    if value.type in self.validatedStructs:
1142                        if value.isconst: # or value.type in self.returnedonly_structs:
1143                            usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
1144                        elif value.type in self.returnedonly_structs:
1145                            usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
1146            # Non-pointer types
1147            else:
1148                # The parameter will not be processes when tagged as 'noautovalidity'
1149                # For the struct case, the struct type will not be validated, but any
1150                # members not tagged as 'noautovalidity' will be validated
1151                if value.noautovalidity:
1152                    # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1153                    self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1154                else:
1155                    vuid_name_tag = structTypeName if structTypeName is not None else funcName
1156                    if value.type in self.structTypes:
1157                        stype = self.structTypes[value.type]
1158                        vuid = self.GetVuid(value.type, "sType-sType")
1159                        undefined_vuid = '"kVUIDUndefined"'
1160                        usedLines.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false, kVUIDUndefined, {});\n'.format(
1161                            funcName, valueDisplayName, valuePrefix, vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
1162                    elif value.type in self.handleTypes:
1163                        if not self.isHandleOptional(value, None):
1164                            usedLines.append('skip |= validate_required_handle("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
1165                    elif value.type in self.flags:
1166                        flagBitsName = value.type.replace('Flags', 'FlagBits')
1167                        if not flagBitsName in self.flagBits:
1168                            vuid = self.GetVuid(vuid_name_tag, "%s-zerobitmask" % (value.name))
1169                            usedLines.append('skip |= validate_reserved_flags("{}", {ppp}"{}"{pps}, {pf}{}, {});\n'.format(funcName, valueDisplayName, value.name, vuid, pf=valuePrefix, **postProcSpec))
1170                        else:
1171                            if value.isoptional:
1172                                flagsRequired = 'false'
1173                                vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
1174                            else:
1175                                flagsRequired = 'true'
1176                                vuid = self.GetVuid(vuid_name_tag, "%s-requiredbitmask" % (value.name))
1177                            allFlagsName = 'All' + flagBitsName
1178                            usedLines.append('skip |= validate_flags("{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {}, false, {});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsRequired, vuid, pf=valuePrefix, **postProcSpec))
1179                    elif value.type in self.flagBits:
1180                        flagsRequired = 'false' if value.isoptional else 'true'
1181                        allFlagsName = 'All' + value.type
1182                        vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
1183                        usedLines.append('skip |= validate_flags("{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {}, true, {});\n'.format(funcName, valueDisplayName, value.type, allFlagsName, value.name, flagsRequired, vuid, pf=valuePrefix, **postProcSpec))
1184                    elif value.isbool:
1185                        usedLines.append('skip |= validate_bool32("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
1186                    elif value.israngedenum:
1187                        vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
1188                        enum_value_list = 'All%sEnums' % value.type
1189                        usedLines.append('skip |= validate_ranged_enum("{}", {ppp}"{}"{pps}, "{}", {}, {}{}, {});\n'.format(funcName, valueDisplayName, value.type, enum_value_list, valuePrefix, value.name, vuid, **postProcSpec))
1190                    # If this is a struct, see if it contains members that need to be checked
1191                    if value.type in self.validatedStructs:
1192                        memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
1193                        memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
1194                        usedLines.append(self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
1195            # Append the parameter check to the function body for the current command
1196            if usedLines:
1197                # Apply special conditional checks
1198                if value.condition:
1199                    usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
1200                lines += usedLines
1201            elif not value.iscount:
1202                # If no expression was generated for this value, it is unreferenced by the validation function, unless
1203                # it is an array count, which is indirectly referenced for array valiadation.
1204                unused.append(value.name)
1205        if not lines:
1206            lines.append('// No xml-driven validation\n')
1207        return lines, unused
1208    #
1209    # Generate the struct member check code from the captured data
1210    def processStructMemberData(self):
1211        indent = self.incIndent(None)
1212        for struct in self.structMembers:
1213            #
1214            # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
1215            lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
1216            if lines:
1217                self.validatedStructs[struct.name] = lines
1218    #
1219    # Generate the command param check code from the captured data
1220    def processCmdData(self):
1221        indent = self.incIndent(None)
1222        for command in self.commands:
1223            # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
1224            startIndex = 0 if command.name == 'vkCreateInstance' else 1
1225            lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
1226            # Cannot validate extension dependencies for device extension APIs having a physical device as their dispatchable object
1227            if (command.name in self.required_extensions) and (self.extension_type != 'device' or command.params[0].type != 'VkPhysicalDevice'):
1228                ext_test = ''
1229                if command.params[0].type in ["VkInstance", "VkPhysicalDevice"] or command.name == 'vkCreateInstance':
1230                    ext_table_type = 'instance'
1231                else:
1232                    ext_table_type = 'device'
1233                for ext in self.required_extensions[command.name]:
1234                    ext_name_define = ''
1235                    ext_enable_name = ''
1236                    for extension in self.registry.extensions:
1237                        if extension.attrib['name'] == ext:
1238                            ext_name_define = extension[0][1].get('name')
1239                            ext_enable_name = ext_name_define.lower()
1240                            ext_enable_name = re.sub('_extension_name', '', ext_enable_name)
1241                            break
1242                    ext_test = 'if (!%s_extensions.%s) skip |= OutputExtensionError("%s", %s);\n' % (ext_table_type, ext_enable_name, command.name, ext_name_define)
1243                    lines.insert(0, ext_test)
1244            if lines:
1245                func_sig = self.getCmdDef(command) + ' {\n'
1246                func_sig = func_sig.split('VKAPI_CALL vk')[1]
1247                cmdDef = 'bool StatelessValidation::PreCallValidate' + func_sig
1248                cmdDef += '%sbool skip = false;\n' % indent
1249                # Insert call to custom-written function if present
1250                if command.name in self.functions_with_manual_checks:
1251                    # Generate parameter list for manual fcn and down-chain calls
1252                    params_text = ''
1253                    for param in command.params:
1254                        params_text += '%s, ' % param.name
1255                    params_text = params_text[:-2] + ');\n'
1256                    cmdDef += '    skip |= manual_PreCallValidate'+ command.name[2:] + '(' + params_text
1257                for line in lines:
1258                    if type(line) is list:
1259                        for sub in line:
1260                            cmdDef += indent + sub
1261                    else:
1262                        cmdDef += indent + line
1263                cmdDef += '%sreturn skip;\n' % indent
1264                cmdDef += '}\n'
1265                self.validation.append(cmdDef)
1266