• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3 -i
2#
3# Copyright (c) 2015-2016 The Khronos Group Inc.
4# Copyright (c) 2015-2016 Valve Corporation
5# Copyright (c) 2015-2016 LunarG, Inc.
6# Copyright (c) 2015-2016 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
22import os,re,sys
23import xml.etree.ElementTree as etree
24from generator import *
25from collections import namedtuple
26
27
28# ParamCheckerGeneratorOptions - subclass of GeneratorOptions.
29#
30# Adds options used by ParamCheckerOutputGenerator object during Parameter
31# validation layer generation.
32#
33# Additional members
34#   prefixText - list of strings to prefix generated header with
35#     (usually a copyright statement + calling convention macros).
36#   protectFile - True if multiple inclusion protection should be
37#     generated (based on the filename) around the entire header.
38#   protectFeature - True if #ifndef..#endif protection should be
39#     generated around a feature interface in the header file.
40#   genFuncPointers - True if function pointer typedefs should be
41#     generated
42#   protectProto - If conditional protection should be generated
43#     around prototype declarations, set to either '#ifdef'
44#     to require opt-in (#ifdef protectProtoStr) or '#ifndef'
45#     to require opt-out (#ifndef protectProtoStr). Otherwise
46#     set to None.
47#   protectProtoStr - #ifdef/#ifndef symbol to use around prototype
48#     declarations, if protectProto is set
49#   apicall - string to use for the function declaration prefix,
50#     such as APICALL on Windows.
51#   apientry - string to use for the calling convention macro,
52#     in typedefs, such as APIENTRY.
53#   apientryp - string to use for the calling convention macro
54#     in function pointer typedefs, such as APIENTRYP.
55#   indentFuncProto - True if prototype declarations should put each
56#     parameter on a separate line
57#   indentFuncPointer - True if typedefed function pointers should put each
58#     parameter on a separate line
59#   alignFuncParam - if nonzero and parameters are being put on a
60#     separate line, align parameter names at the specified column
61class ParamCheckerGeneratorOptions(GeneratorOptions):
62    def __init__(self,
63                 filename = None,
64                 directory = '.',
65                 apiname = None,
66                 profile = None,
67                 versions = '.*',
68                 emitversions = '.*',
69                 defaultExtensions = None,
70                 addExtensions = None,
71                 removeExtensions = None,
72                 sortProcedure = regSortFeatures,
73                 prefixText = "",
74                 genFuncPointers = True,
75                 protectFile = True,
76                 protectFeature = True,
77                 protectProto = None,
78                 protectProtoStr = None,
79                 apicall = '',
80                 apientry = '',
81                 apientryp = '',
82                 indentFuncProto = True,
83                 indentFuncPointer = False,
84                 alignFuncParam = 0):
85        GeneratorOptions.__init__(self, filename, directory, apiname, profile,
86                                  versions, emitversions, defaultExtensions,
87                                  addExtensions, removeExtensions, sortProcedure)
88        self.prefixText      = prefixText
89        self.genFuncPointers = genFuncPointers
90        self.protectFile     = protectFile
91        self.protectFeature  = protectFeature
92        self.protectProto    = protectProto
93        self.protectProtoStr = protectProtoStr
94        self.apicall         = apicall
95        self.apientry        = apientry
96        self.apientryp       = apientryp
97        self.indentFuncProto = indentFuncProto
98        self.indentFuncPointer = indentFuncPointer
99        self.alignFuncParam  = alignFuncParam
100
101# ParamCheckerOutputGenerator - 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 ParamCheckerOutputGenerator(OutputGenerator):
118    """Generate ParamChecker 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        # Commands to ignore
128        self.blacklist = [
129            'vkGetInstanceProcAddr',
130            'vkGetDeviceProcAddr',
131            'vkEnumerateInstanceLayerProperties',
132            'vkEnumerateInstanceExtensionsProperties',
133            'vkEnumerateDeviceLayerProperties',
134            'vkEnumerateDeviceExtensionsProperties',
135            'vkCreateDebugReportCallbackEXT',
136            'vkDebugReportMessageEXT']
137        # Validation conditions for some special case struct members that are conditionally validated
138        self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
139        # Header version
140        self.headerVersion = None
141        # Internal state - accumulators for different inner block text
142        self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
143        self.structNames = []                             # List of Vulkan struct typenames
144        self.stypes = []                                  # Values from the VkStructureType enumeration
145        self.structTypes = dict()                         # Map of Vulkan struct typename to required VkStructureType
146        self.handleTypes = set()                          # Set of handle type names
147        self.commands = []                                # List of CommandData records for all Vulkan commands
148        self.structMembers = []                           # List of StructMemberData records for all Vulkan structs
149        self.validatedStructs = dict()                    # Map of structs type names to generated validation code for that struct type
150        self.enumRanges = dict()                          # Map of enum name to BEGIN/END range values
151        self.flags = set()                                # Map of flags typenames
152        self.flagBits = dict()                            # Map of flag bits typename to list of values
153        # Named tuples to store struct and command data
154        self.StructType = namedtuple('StructType', ['name', 'value'])
155        self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
156                                                        'isconst', 'isoptional', 'iscount', 'noautovalidity', 'len', 'extstructs',
157                                                        'condition', 'cdecl'])
158        self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl'])
159        self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
160    #
161    def incIndent(self, indent):
162        inc = ' ' * self.INDENT_SPACES
163        if indent:
164            return indent + inc
165        return inc
166    #
167    def decIndent(self, indent):
168        if indent and (len(indent) > self.INDENT_SPACES):
169            return indent[:-self.INDENT_SPACES]
170        return ''
171    #
172    def beginFile(self, genOpts):
173        OutputGenerator.beginFile(self, genOpts)
174        # C-specific
175        #
176        # User-supplied prefix text, if any (list of strings)
177        if (genOpts.prefixText):
178            for s in genOpts.prefixText:
179                write(s, file=self.outFile)
180        #
181        # Multiple inclusion protection & C++ wrappers.
182        if (genOpts.protectFile and self.genOpts.filename):
183            headerSym = re.sub('\.h', '_H', os.path.basename(self.genOpts.filename)).upper()
184            write('#ifndef', headerSym, file=self.outFile)
185            write('#define', headerSym, '1', file=self.outFile)
186            self.newline()
187        #
188        # Headers
189        write('#include <string>', file=self.outFile)
190        self.newline()
191        write('#include "vulkan/vulkan.h"', file=self.outFile)
192        write('#include "vk_layer_extension_utils.h"', file=self.outFile)
193        write('#include "parameter_validation_utils.h"', file=self.outFile)
194        #
195        # Macros
196        self.newline()
197        write('#ifndef UNUSED_PARAMETER', file=self.outFile)
198        write('#define UNUSED_PARAMETER(x) (void)(x)', file=self.outFile)
199        write('#endif // UNUSED_PARAMETER', file=self.outFile)
200        #
201        # Namespace
202        self.newline()
203        write('namespace parameter_validation {', file = self.outFile)
204    def endFile(self):
205        # C-specific
206        self.newline()
207        # Namespace
208        write('} // namespace parameter_validation', file = self.outFile)
209        # Finish C++ wrapper and multiple inclusion protection
210        if (self.genOpts.protectFile and self.genOpts.filename):
211            self.newline()
212            write('#endif', file=self.outFile)
213        # Finish processing in superclass
214        OutputGenerator.endFile(self)
215    def beginFeature(self, interface, emit):
216        # Start processing in superclass
217        OutputGenerator.beginFeature(self, interface, emit)
218        # C-specific
219        # Accumulate includes, defines, types, enums, function pointer typedefs,
220        # end function prototypes separately for this feature. They're only
221        # printed in endFeature().
222        self.headerVersion = None
223        self.sections = dict([(section, []) for section in self.ALL_SECTIONS])
224        self.structNames = []
225        self.stypes = []
226        self.structTypes = dict()
227        self.handleTypes = set()
228        self.commands = []
229        self.structMembers = []
230        self.validatedStructs = dict()
231        self.enumRanges = dict()
232        self.flags = set()
233        self.flagBits = dict()
234    def endFeature(self):
235        # C-specific
236        # Actually write the interface to the output file.
237        if (self.emit):
238            self.newline()
239            # If type declarations are needed by other features based on
240            # this one, it may be necessary to suppress the ExtraProtect,
241            # or move it below the 'for section...' loop.
242            if (self.featureExtraProtect != None):
243                write('#ifdef', self.featureExtraProtect, file=self.outFile)
244            # Generate the struct member checking code from the captured data
245            self.processStructMemberData()
246            # Generate the command parameter checking code from the captured data
247            self.processCmdData()
248            # Write the declaration for the HeaderVersion
249            if self.headerVersion:
250                write('const uint32_t GeneratedHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
251                self.newline()
252            # Write the declarations for the VkFlags values combining all flag bits
253            for flag in sorted(self.flags):
254                flagBits = flag.replace('Flags', 'FlagBits')
255                if flagBits in self.flagBits:
256                    bits = self.flagBits[flagBits]
257                    decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
258                    for bit in bits[1:]:
259                        decl += '|' + bit
260                    decl += ';'
261                    write(decl, file=self.outFile)
262            self.newline()
263            # Write the parameter validation code to the file
264            if (self.sections['command']):
265                if (self.genOpts.protectProto):
266                    write(self.genOpts.protectProto,
267                          self.genOpts.protectProtoStr, file=self.outFile)
268                write('\n'.join(self.sections['command']), end='', file=self.outFile)
269            if (self.featureExtraProtect != None):
270                write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile)
271            else:
272                self.newline()
273        # Finish processing in superclass
274        OutputGenerator.endFeature(self)
275    #
276    # Append a definition to the specified section
277    def appendSection(self, section, text):
278        # self.sections[section].append('SECTION: ' + section + '\n')
279        self.sections[section].append(text)
280    #
281    # Type generation
282    def genType(self, typeinfo, name):
283        OutputGenerator.genType(self, typeinfo, name)
284        typeElem = typeinfo.elem
285        # If the type is a struct type, traverse the imbedded <member> tags
286        # generating a structure. Otherwise, emit the tag text.
287        category = typeElem.get('category')
288        if (category == 'struct' or category == 'union'):
289            self.structNames.append(name)
290            self.genStruct(typeinfo, name)
291        elif (category == 'handle'):
292            self.handleTypes.add(name)
293        elif (category == 'bitmask'):
294            self.flags.add(name)
295        elif (category == 'define'):
296            if name == 'VK_HEADER_VERSION':
297                nameElem = typeElem.find('name')
298                self.headerVersion = noneStr(nameElem.tail).strip()
299    #
300    # Struct parameter check generation.
301    # This is a special case of the <type> tag where the contents are
302    # interpreted as a set of <member> tags instead of freeform C
303    # C type declarations. The <member> tags are just like <param>
304    # tags - they are a declaration of a struct or union member.
305    # Only simple member declarations are supported (no nested
306    # structs etc.)
307    def genStruct(self, typeinfo, typeName):
308        OutputGenerator.genStruct(self, typeinfo, typeName)
309        conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
310        members = typeinfo.elem.findall('.//member')
311        #
312        # Iterate over members once to get length parameters for arrays
313        lens = set()
314        for member in members:
315            len = self.getLen(member)
316            if len:
317                lens.add(len)
318        #
319        # Generate member info
320        membersInfo = []
321        for member in members:
322            # Get the member's type and name
323            info = self.getTypeNameTuple(member)
324            type = info[0]
325            name = info[1]
326            stypeValue = ''
327            cdecl = self.makeCParamDecl(member, 0)
328            # Process VkStructureType
329            if type == 'VkStructureType':
330                # Extract the required struct type value from the comments
331                # embedded in the original text defining the 'typeinfo' element
332                rawXml = etree.tostring(typeinfo.elem).decode('ascii')
333                result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml)
334                if result:
335                    value = result.group(0)
336                else:
337                    value = self.genVkStructureType(typeName)
338                # Store the required type value
339                self.structTypes[typeName] = self.StructType(name=name, value=value)
340            #
341            # Store pointer/array/string info
342            # Check for parameter name in lens set
343            iscount = False
344            if name in lens:
345                iscount = True
346            # The pNext members are not tagged as optional, but are treated as
347            # optional for parameter NULL checks.  Static array members
348            # are also treated as optional to skip NULL pointer validation, as
349            # they won't be NULL.
350            isstaticarray = self.paramIsStaticArray(member)
351            isoptional = False
352            if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
353                isoptional = True
354            membersInfo.append(self.CommandParam(type=type, name=name,
355                                                ispointer=self.paramIsPointer(member),
356                                                isstaticarray=isstaticarray,
357                                                isbool=True if type == 'VkBool32' else False,
358                                                israngedenum=True if type in self.enumRanges else False,
359                                                isconst=True if 'const' in cdecl else False,
360                                                isoptional=isoptional,
361                                                iscount=iscount,
362                                                noautovalidity=True if member.attrib.get('noautovalidity') is not None else False,
363                                                len=self.getLen(member),
364                                                extstructs=member.attrib.get('validextensionstructs') if name == 'pNext' else None,
365                                                condition=conditions[name] if conditions and name in conditions else None,
366                                                cdecl=cdecl))
367        self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
368    #
369    # Capture group (e.g. C "enum" type) info to be used for
370    # param check code generation.
371    # These are concatenated together with other types.
372    def genGroup(self, groupinfo, groupName):
373        OutputGenerator.genGroup(self, groupinfo, groupName)
374        groupElem = groupinfo.elem
375        #
376        # Store the sType values
377        if groupName == 'VkStructureType':
378            for elem in groupElem.findall('enum'):
379                self.stypes.append(elem.get('name'))
380        elif 'FlagBits' in groupName:
381            bits = []
382            for elem in groupElem.findall('enum'):
383                bits.append(elem.get('name'))
384            if bits:
385                self.flagBits[groupName] = bits
386        else:
387            # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
388            expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
389            expandPrefix = expandName
390            expandSuffix = ''
391            expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
392            if expandSuffixMatch:
393                expandSuffix = '_' + expandSuffixMatch.group()
394                # Strip off the suffix from the prefix
395                expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
396            isEnum = ('FLAG_BITS' not in expandPrefix)
397            if isEnum:
398                self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
399    #
400    # Capture command parameter info to be used for param
401    # check code generation.
402    def genCmd(self, cmdinfo, name):
403        OutputGenerator.genCmd(self, cmdinfo, name)
404        if name not in self.blacklist:
405            params = cmdinfo.elem.findall('param')
406            # Get list of array lengths
407            lens = set()
408            for param in params:
409                len = self.getLen(param)
410                if len:
411                    lens.add(len)
412            # Get param info
413            paramsInfo = []
414            for param in params:
415                paramInfo = self.getTypeNameTuple(param)
416                cdecl = self.makeCParamDecl(param, 0)
417                # Check for parameter name in lens set
418                iscount = False
419                if paramInfo[1] in lens:
420                    iscount = True
421                paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
422                                                    ispointer=self.paramIsPointer(param),
423                                                    isstaticarray=self.paramIsStaticArray(param),
424                                                    isbool=True if paramInfo[0] == 'VkBool32' else False,
425                                                    israngedenum=True if paramInfo[0] in self.enumRanges else False,
426                                                    isconst=True if 'const' in cdecl else False,
427                                                    isoptional=self.paramIsOptional(param),
428                                                    iscount=iscount,
429                                                    noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
430                                                    len=self.getLen(param),
431                                                    extstructs=None,
432                                                    condition=None,
433                                                    cdecl=cdecl))
434            self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0]))
435    #
436    # Check if the parameter passed in is a pointer
437    def paramIsPointer(self, param):
438        ispointer = 0
439        paramtype = param.find('type')
440        if (paramtype.tail is not None) and ('*' in paramtype.tail):
441            ispointer = paramtype.tail.count('*')
442        elif paramtype.text[:4] == 'PFN_':
443            # Treat function pointer typedefs as a pointer to a single value
444            ispointer = 1
445        return ispointer
446    #
447    # Check if the parameter passed in is a static array
448    def paramIsStaticArray(self, param):
449        isstaticarray = 0
450        paramname = param.find('name')
451        if (paramname.tail is not None) and ('[' in paramname.tail):
452            isstaticarray = paramname.tail.count('[')
453        return isstaticarray
454    #
455    # Check if the parameter passed in is optional
456    # Returns a list of Boolean values for comma separated len attributes (len='false,true')
457    def paramIsOptional(self, param):
458        # See if the handle is optional
459        isoptional = False
460        # Simple, if it's optional, return true
461        optString = param.attrib.get('optional')
462        if optString:
463            if optString == 'true':
464                isoptional = True
465            elif ',' in optString:
466                opts = []
467                for opt in optString.split(','):
468                    val = opt.strip()
469                    if val == 'true':
470                        opts.append(True)
471                    elif val == 'false':
472                        opts.append(False)
473                    else:
474                        print('Unrecognized len attribute value',val)
475                isoptional = opts
476        return isoptional
477    #
478    # Check if the handle passed in is optional
479    # Uses the same logic as ValidityOutputGenerator.isHandleOptional
480    def isHandleOptional(self, param, lenParam):
481        # Simple, if it's optional, return true
482        if param.isoptional:
483            return True
484        # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
485        if param.noautovalidity:
486            return True
487        # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
488        if lenParam and lenParam.isoptional:
489            return True
490        return False
491    #
492    # Generate a VkStructureType based on a structure typename
493    def genVkStructureType(self, typename):
494        # Add underscore between lowercase then uppercase
495        value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', typename)
496        # Change to uppercase
497        value = value.upper()
498        # Add STRUCTURE_TYPE_
499        return re.sub('VK_', 'VK_STRUCTURE_TYPE_', value)
500    #
501    # Get the cached VkStructureType value for the specified struct typename, or generate a VkStructureType
502    # value assuming the struct is defined by a different feature
503    def getStructType(self, typename):
504        value = None
505        if typename in self.structTypes:
506            value = self.structTypes[typename].value
507        else:
508            value = self.genVkStructureType(typename)
509            self.logMsg('diag', 'ParameterValidation: Generating {} for {} structure type that was not defined by the current feature'.format(value, typename))
510        return value
511    #
512    # Retrieve the value of the len tag
513    def getLen(self, param):
514        result = None
515        len = param.attrib.get('len')
516        if len and len != 'null-terminated':
517            # For string arrays, 'len' can look like 'count,null-terminated',
518            # indicating that we have a null terminated array of strings.  We
519            # strip the null-terminated from the 'len' field and only return
520            # the parameter specifying the string count
521            if 'null-terminated' in len:
522                result = len.split(',')[0]
523            else:
524                result = len
525            result = str(result).replace('::', '->')
526        return result
527    #
528    # Retrieve the type and name for a parameter
529    def getTypeNameTuple(self, param):
530        type = ''
531        name = ''
532        for elem in param:
533            if elem.tag == 'type':
534                type = noneStr(elem.text)
535            elif elem.tag == 'name':
536                name = noneStr(elem.text)
537        return (type, name)
538    #
539    # Find a named parameter in a parameter list
540    def getParamByName(self, params, name):
541        for param in params:
542            if param.name == name:
543                return param
544        return None
545    #
546    # Extract length values from latexmath.  Currently an inflexible solution that looks for specific
547    # patterns that are found in vk.xml.  Will need to be updated when new patterns are introduced.
548    def parseLateXMath(self, source):
549        name = 'ERROR'
550        decoratedName = 'ERROR'
551        if 'mathit' in source:
552            # Matches expressions similar to 'latexmath:[$\lceil{\mathit{rasterizationSamples} \over 32}\rceil$]'
553            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)
554            if not match or match.group(1) != match.group(4):
555                raise 'Unrecognized latexmath expression'
556            name = match.group(2)
557            decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
558        else:
559            # Matches expressions similar to 'latexmath : [$dataSize \over 4$]'
560            match = re.match(r'latexmath\s*\:\s*\[\s*\$\s*(\w+)\s*\\over\s*(\d+)\s*\$\s*\]', source)
561            name = match.group(1)
562            decoratedName = '{}/{}'.format(*match.group(1, 2))
563        return name, decoratedName
564    #
565    # Get the length paramater record for the specified parameter name
566    def getLenParam(self, params, name):
567        lenParam = None
568        if name:
569            if '->' in name:
570                # The count is obtained by dereferencing a member of a struct parameter
571                lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
572                                             isstaticarray=None, isoptional=False, type=None, noautovalidity=False, len=None, extstructs=None,
573                                             condition=None, cdecl=None)
574            elif 'latexmath' in name:
575                lenName, decoratedName = self.parseLateXMath(name)
576                lenParam = self.getParamByName(params, lenName)
577                # TODO: Zero-check the result produced by the equation?
578                # Copy the stored len parameter entry and overwrite the name with the processed latexmath equation
579                #param = self.getParamByName(params, lenName)
580                #lenParam = self.CommandParam(name=decoratedName, iscount=param.iscount, ispointer=param.ispointer,
581                #                             isoptional=param.isoptional, type=param.type, len=param.len,
582                #                             isstaticarray=param.isstaticarray, extstructs=param.extstructs,
583                #                             noautovalidity=True, condition=None, cdecl=param.cdecl)
584            else:
585                lenParam = self.getParamByName(params, name)
586        return lenParam
587    #
588    # Convert a vulkan.h command declaration into a parameter_validation.h definition
589    def getCmdDef(self, cmd):
590        #
591        # Strip the trailing ';' and split into individual lines
592        lines = cmd.cdecl[:-1].split('\n')
593        # Replace Vulkan prototype
594        lines[0] = 'static bool parameter_validation_' + cmd.name + '('
595        # Replace the first argument with debug_report_data, when the first
596        # argument is a handle (not vkCreateInstance)
597        reportData = '    debug_report_data*'.ljust(self.genOpts.alignFuncParam) + 'report_data,'
598        if cmd.name != 'vkCreateInstance':
599            lines[1] = reportData
600        else:
601            lines.insert(1, reportData)
602        return '\n'.join(lines)
603    #
604    # Generate the code to check for a NULL dereference before calling the
605    # validation function
606    def genCheckedLengthCall(self, name, exprs):
607        count = name.count('->')
608        if count:
609            checkedExpr = []
610            localIndent = ''
611            elements = name.split('->')
612            # Open the if expression blocks
613            for i in range(0, count):
614                checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
615                localIndent = self.incIndent(localIndent)
616            # Add the validation expression
617            for expr in exprs:
618                checkedExpr.append(localIndent + expr)
619            # Close the if blocks
620            for i in range(0, count):
621                localIndent = self.decIndent(localIndent)
622                checkedExpr.append(localIndent + '}\n')
623            return [checkedExpr]
624        # No if statements were required
625        return exprs
626    #
627    # Generate code to check for a specific condition before executing validation code
628    def genConditionalCall(self, prefix, condition, exprs):
629        checkedExpr = []
630        localIndent = ''
631        formattedCondition = condition.format(prefix)
632        checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
633        checkedExpr.append(localIndent + '{\n')
634        localIndent = self.incIndent(localIndent)
635        for expr in exprs:
636            checkedExpr.append(localIndent + expr)
637        localIndent = self.decIndent(localIndent)
638        checkedExpr.append(localIndent + '}\n')
639        return [checkedExpr]
640    #
641    # Generate the sType check string
642    def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
643        checkExpr = []
644        stype = self.structTypes[value.type]
645        if lenValue:
646            # This is an array with a pointer to a count value
647            if lenValue.ispointer:
648                # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
649                checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {});\n'.format(
650                    funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
651            # This is an array with an integer count value
652            else:
653                checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {});\n'.format(
654                    funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix, **postProcSpec))
655        # This is an individual struct
656        else:
657            checkExpr.append('skipCall |= validate_struct_type(report_data, "{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {});\n'.format(
658                funcPrintName, valuePrintName, prefix, valueRequired, vn=value.name, sv=stype.value, **postProcSpec))
659        return checkExpr
660    #
661    # Generate the handle check string
662    def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
663        checkExpr = []
664        if lenValue:
665            if lenValue.ispointer:
666                # This is assumed to be an output array with a pointer to a count value
667                raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
668            else:
669                # This is an array with an integer count value
670                checkExpr.append('skipCall |= validate_handle_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
671                    funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
672        else:
673            # This is assumed to be an output handle pointer
674            raise('Unsupported parameter validation case: Output handles are not NULL checked')
675        return checkExpr
676    #
677    # Generate check string for an array of VkFlags values
678    def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
679        checkExpr = []
680        flagBitsName = value.type.replace('Flags', 'FlagBits')
681        if not flagBitsName in self.flagBits:
682            raise('Unsupported parameter validation case: array of reserved VkFlags')
683        else:
684            allFlags = 'All' + flagBitsName
685            checkExpr.append('skipCall |= validate_flags_array(report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcPrintName, lenPrintName, valuePrintName, flagBitsName, allFlags, lenValue.name, value.name, lenValueRequired, valueRequired, pf=prefix, **postProcSpec))
686        return checkExpr
687    #
688    # Generate pNext check string
689    def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec):
690        checkExpr = []
691        # Generate an array of acceptable VkStructureType values for pNext
692        extStructCount = 0
693        extStructVar = 'NULL'
694        extStructNames = 'NULL'
695        if value.extstructs:
696            structs = value.extstructs.split(',')
697            checkExpr.append('const VkStructureType allowedStructs[] = {' + ', '.join([self.getStructType(s) for s in structs]) + '};\n')
698            extStructCount = 'ARRAY_SIZE(allowedStructs)'
699            extStructVar = 'allowedStructs'
700            extStructNames = '"' + ', '.join(structs) + '"'
701        checkExpr.append('skipCall |= validate_struct_pnext(report_data, "{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedHeaderVersion);\n'.format(
702            funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, **postProcSpec))
703        return checkExpr
704    #
705    # Generate the pointer check string
706    def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
707        checkExpr = []
708        if lenValue:
709            # This is an array with a pointer to a count value
710            if lenValue.ispointer:
711                # If count and array parameters are optional, there will be no validation
712                if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
713                    # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
714                    checkExpr.append('skipCall |= validate_array(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {});\n'.format(
715                        funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
716            # This is an array with an integer count value
717            else:
718                # If count and array parameters are optional, there will be no validation
719                if valueRequired == 'true' or lenValueRequired == 'true':
720                    # Arrays of strings receive special processing
721                    validationFuncName = 'validate_array' if value.type != 'char' else 'validate_string_array'
722                    checkExpr.append('skipCall |= {}(report_data, "{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
723                        validationFuncName, funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
724            if checkExpr:
725                if lenValue and ('->' in lenValue.name):
726                    # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
727                    checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
728        # This is an individual struct that is not allowed to be NULL
729        elif not value.isoptional:
730            # Function pointers need a reinterpret_cast to void*
731            if value.type[:4] == 'PFN_':
732                checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", {ppp}"{}"{pps}, reinterpret_cast<const void*>({}{}));\n'.format(funcPrintName, valuePrintName, prefix, value.name, **postProcSpec))
733            else:
734                checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcPrintName, valuePrintName, prefix, value.name, **postProcSpec))
735        return checkExpr
736    #
737    # Process struct member validation code, performing name suibstitution if required
738    def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
739        # Build format specifier list
740        kwargs = {}
741        if '{postProcPrefix}' in line:
742            # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
743            if type(memberDisplayNamePrefix) is tuple:
744                kwargs['postProcPrefix'] = 'ParameterName('
745            else:
746                kwargs['postProcPrefix'] = postProcSpec['ppp']
747        if '{postProcSuffix}' in line:
748            # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
749            if type(memberDisplayNamePrefix) is tuple:
750                kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
751            else:
752                kwargs['postProcSuffix'] = postProcSpec['pps']
753        if '{postProcInsert}' in line:
754            # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
755            if type(memberDisplayNamePrefix) is tuple:
756                kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
757            else:
758                kwargs['postProcInsert'] = postProcSpec['ppi']
759        if '{funcName}' in line:
760            kwargs['funcName'] = funcName
761        if '{valuePrefix}' in line:
762            kwargs['valuePrefix'] = memberNamePrefix
763        if '{displayNamePrefix}' in line:
764            # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
765            if type(memberDisplayNamePrefix) is tuple:
766                kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
767            else:
768                kwargs['displayNamePrefix'] = memberDisplayNamePrefix
769
770        if kwargs:
771            # Need to escape the C++ curly braces
772            if 'IndexVector' in line:
773                line = line.replace('IndexVector{ ', 'IndexVector{{ ')
774                line = line.replace(' }),', ' }}),')
775            return line.format(**kwargs)
776        return line
777    #
778    # Process struct validation code for inclusion in function or parent struct validation code
779    def expandStructCode(self, lines, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
780        for line in lines:
781            if output:
782                output[-1] += '\n'
783            if type(line) is list:
784                for sub in line:
785                    output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
786            else:
787                output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
788        return output
789    #
790    # Process struct pointer/array validation code, perfoeming name substitution if required
791    def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
792        expr = []
793        expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
794        expr.append('{')
795        indent = self.incIndent(None)
796        if lenValue:
797            # Need to process all elements in the array
798            indexName = lenValue.name.replace('Count', 'Index')
799            expr[-1] += '\n'
800            expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
801            expr.append(indent + '{')
802            indent = self.incIndent(indent)
803            # Prefix for value name to display in error message
804            memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
805            memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
806        else:
807            memberNamePrefix = '{}{}->'.format(prefix, value.name)
808            memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
809        #
810        # Expand the struct validation lines
811        expr = self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
812        #
813        if lenValue:
814            # Close if and for scopes
815            indent = self.decIndent(indent)
816            expr.append(indent + '}\n')
817        expr.append('}\n')
818        return expr
819    #
820    # Generate the parameter checking code
821    def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
822        lines = []    # Generated lines of code
823        unused = []   # Unused variable names
824        for value in values:
825            usedLines = []
826            lenParam = None
827            #
828            # 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.
829            postProcSpec = {}
830            postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
831            postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
832            postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
833            #
834            # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name
835            valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
836            #
837            # Check for NULL pointers, ignore the inout count parameters that
838            # will be validated with their associated array
839            if (value.ispointer or value.isstaticarray) and not value.iscount:
840                #
841                # Parameters for function argument generation
842                req = 'true'    # Paramerter cannot be NULL
843                cpReq = 'true'  # Count pointer cannot be NULL
844                cvReq = 'true'  # Count value cannot be 0
845                lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
846                #
847                # Generate required/optional parameter strings for the pointer and count values
848                if value.isoptional:
849                    req = 'false'
850                if value.len:
851                    # The parameter is an array with an explicit count parameter
852                    lenParam = self.getLenParam(values, value.len)
853                    lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
854                    if lenParam.ispointer:
855                        # Count parameters that are pointers are inout
856                        if type(lenParam.isoptional) is list:
857                            if lenParam.isoptional[0]:
858                                cpReq = 'false'
859                            if lenParam.isoptional[1]:
860                                cvReq = 'false'
861                        else:
862                            if lenParam.isoptional:
863                                cpReq = 'false'
864                    else:
865                        if lenParam.isoptional:
866                            cvReq = 'false'
867                #
868                # The parameter will not be processes when tagged as 'noautovalidity'
869                # For the pointer to struct case, the struct pointer will not be validated, but any
870                # members not tagged as 'noatuvalidity' will be validated
871                if value.noautovalidity:
872                    # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
873                    self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
874                else:
875                    #
876                    # If this is a pointer to a struct with an sType field, verify the type
877                    if value.type in self.structTypes:
878                        usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
879                    # 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
880                    elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
881                        usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
882                    elif value.type in self.flags and value.isconst:
883                        usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
884                    elif value.isbool and value.isconst:
885                        usedLines.append('skipCall |= validate_bool32_array(report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
886                    elif value.israngedenum and value.isconst:
887                        enumRange = self.enumRanges[value.type]
888                        usedLines.append('skipCall |= validate_ranged_enum_array(report_data, "{}", {ppp}"{}"{pps}, {ppp}"{}"{pps}, "{}", {}, {}, {pf}{}, {pf}{}, {}, {});\n'.format(funcName, lenDisplayName, valueDisplayName, value.type, enumRange[0], enumRange[1], lenParam.name, value.name, cvReq, req, pf=valuePrefix, **postProcSpec))
889                    elif value.name == 'pNext':
890                        # We need to ignore VkDeviceCreateInfo and VkInstanceCreateInfo, as the loader manipulates them in a way that is not documented in vk.xml
891                        if not structTypeName in ['VkDeviceCreateInfo', 'VkInstanceCreateInfo']:
892                            usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec)
893                    else:
894                        usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
895                    #
896                    # If this is a pointer to a struct (input), see if it contains members that need to be checked
897                    if value.type in self.validatedStructs and value.isconst:
898                        usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
899            # Non-pointer types
900            else:
901                #
902                # The parameter will not be processes when tagged as 'noautovalidity'
903                # For the struct case, the struct type will not be validated, but any
904                # members not tagged as 'noatuvalidity' will be validated
905                if value.noautovalidity:
906                    # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
907                    self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
908                else:
909                    if value.type in self.structTypes:
910                        stype = self.structTypes[value.type]
911                        usedLines.append('skipCall |= validate_struct_type(report_data, "{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false);\n'.format(
912                            funcName, valueDisplayName, valuePrefix, vn=value.name, sv=stype.value, **postProcSpec))
913                    elif value.type in self.handleTypes:
914                        if not self.isHandleOptional(value, None):
915                            usedLines.append('skipCall |= validate_required_handle(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
916                    elif value.type in self.flags:
917                        flagBitsName = value.type.replace('Flags', 'FlagBits')
918                        if not flagBitsName in self.flagBits:
919                            usedLines.append('skipCall |= validate_reserved_flags(report_data, "{}", {ppp}"{}"{pps}, {pf}{});\n'.format(funcName, valueDisplayName, value.name, pf=valuePrefix, **postProcSpec))
920                        else:
921                            flagsRequired = 'false' if value.isoptional else 'true'
922                            allFlagsName = 'All' + flagBitsName
923                            usedLines.append('skipCall |= validate_flags(report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsRequired, pf=valuePrefix, **postProcSpec))
924                    elif value.isbool:
925                        usedLines.append('skipCall |= validate_bool32(report_data, "{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
926                    elif value.israngedenum:
927                        enumRange = self.enumRanges[value.type]
928                        usedLines.append('skipCall |= validate_ranged_enum(report_data, "{}", {ppp}"{}"{pps}, "{}", {}, {}, {}{});\n'.format(funcName, valueDisplayName, value.type, enumRange[0], enumRange[1], valuePrefix, value.name, **postProcSpec))
929                    #
930                    # If this is a struct, see if it contains members that need to be checked
931                    if value.type in self.validatedStructs:
932                        memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
933                        memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
934                        usedLines.append(self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
935            #
936            # Append the parameter check to the function body for the current command
937            if usedLines:
938                # Apply special conditional checks
939                if value.condition:
940                    usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
941                lines += usedLines
942            elif not value.iscount:
943                # If no expression was generated for this value, it is unreferenced by the validation function, unless
944                # it is an array count, which is indirectly referenced for array valiadation.
945                unused.append(value.name)
946        return lines, unused
947    #
948    # Generate the struct member check code from the captured data
949    def processStructMemberData(self):
950        indent = self.incIndent(None)
951        for struct in self.structMembers:
952            #
953            # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
954            lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
955            if lines:
956                self.validatedStructs[struct.name] = lines
957    #
958    # Generate the command param check code from the captured data
959    def processCmdData(self):
960        indent = self.incIndent(None)
961        for command in self.commands:
962            # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
963            startIndex = 0 if command.name == 'vkCreateInstance' else 1
964            lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
965            if lines:
966                cmdDef = self.getCmdDef(command) + '\n'
967                cmdDef += '{\n'
968                # Process unused parameters, Ignoring the first dispatch handle parameter, which is not
969                # processed by parameter_validation (except for vkCreateInstance, which does not have a
970                # handle as its first parameter)
971                if unused:
972                    for name in unused:
973                        cmdDef += indent + 'UNUSED_PARAMETER({});\n'.format(name)
974                    if len(unused) > 0:
975                        cmdDef += '\n'
976                cmdDef += indent + 'bool skipCall = false;\n'
977                for line in lines:
978                    cmdDef += '\n'
979                    if type(line) is list:
980                        for sub in line:
981                            cmdDef += indent + sub
982                    else:
983                        cmdDef += indent + line
984                cmdDef += '\n'
985                cmdDef += indent + 'return skipCall;\n'
986                cmdDef += '}\n'
987                self.appendSection('command', cmdDef)
988