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