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