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