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