blob: a5f174c1faed459eafe1b3bb13a60d0476257bbc [file] [log] [blame]
Mark Lobodzinski85672672016-10-13 08:36:42 -06001#!/usr/bin/python3 -i
2#
Shahbaz Youssefi23aee922019-01-11 14:04:49 -05003# 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.
Mark Lobodzinski85672672016-10-13 08:36:42 -06007#
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>
Mark Lobodzinski26112592017-05-30 12:02:17 -060021# Author: Mark Lobodzinski <mark@lunarg.com>
Dave Houlton413a6782018-05-22 13:01:54 -060022# Author: Dave Houlton <daveh@lunarg.com>
Mark Lobodzinski85672672016-10-13 08:36:42 -060023
Dave Houlton413a6782018-05-22 13:01:54 -060024import os,re,sys,string,json
Mark Lobodzinski85672672016-10-13 08:36:42 -060025import xml.etree.ElementTree as etree
26from generator import *
27from collections import namedtuple
Mark Lobodzinski62f71562017-10-24 13:41:18 -060028from common_codegen import *
Mark Lobodzinski06954ea2017-06-21 12:21:45 -060029
Jamie Madill8d4cda22017-11-08 13:40:09 -050030# This is a workaround to use a Python 2.7 and 3.x compatible syntax.
31from io import open
Mark Lobodzinski85672672016-10-13 08:36:42 -060032
Mark Lobodzinskid4950072017-08-01 13:02:20 -060033# ParameterValidationGeneratorOptions - subclass of GeneratorOptions.
Mark Lobodzinski85672672016-10-13 08:36:42 -060034#
Mark Lobodzinskid4950072017-08-01 13:02:20 -060035# Adds options used by ParameterValidationOutputGenerator object during Parameter validation layer generation.
Mark Lobodzinski85672672016-10-13 08:36:42 -060036#
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
Mark Lobodzinskid4950072017-08-01 13:02:20 -060065class ParameterValidationGeneratorOptions(GeneratorOptions):
Mark Lobodzinski85672672016-10-13 08:36:42 -060066 def __init__(self,
Mike Schuchardt21638df2019-03-16 10:52:02 -070067 conventions = None,
Mark Lobodzinski85672672016-10-13 08:36:42 -060068 filename = None,
69 directory = '.',
70 apiname = None,
71 profile = None,
72 versions = '.*',
73 emitversions = '.*',
74 defaultExtensions = None,
75 addExtensions = None,
76 removeExtensions = None,
Mark Lobodzinski62f71562017-10-24 13:41:18 -060077 emitExtensions = None,
Mark Lobodzinski85672672016-10-13 08:36:42 -060078 sortProcedure = regSortFeatures,
79 prefixText = "",
Mark Lobodzinski85672672016-10-13 08:36:42 -060080 apicall = '',
81 apientry = '',
82 apientryp = '',
83 indentFuncProto = True,
84 indentFuncPointer = False,
Mark Lobodzinski62f71562017-10-24 13:41:18 -060085 alignFuncParam = 0,
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -060086 expandEnumerants = True,
87 valid_usage_path = ''):
Mike Schuchardt21638df2019-03-16 10:52:02 -070088 GeneratorOptions.__init__(self, conventions, filename, directory, apiname, profile,
Mark Lobodzinski85672672016-10-13 08:36:42 -060089 versions, emitversions, defaultExtensions,
Mark Lobodzinski62f71562017-10-24 13:41:18 -060090 addExtensions, removeExtensions, emitExtensions, sortProcedure)
Mark Lobodzinski85672672016-10-13 08:36:42 -060091 self.prefixText = prefixText
Mark Lobodzinski85672672016-10-13 08:36:42 -060092 self.apicall = apicall
93 self.apientry = apientry
94 self.apientryp = apientryp
95 self.indentFuncProto = indentFuncProto
96 self.indentFuncPointer = indentFuncPointer
97 self.alignFuncParam = alignFuncParam
Mark Lobodzinski62f71562017-10-24 13:41:18 -060098 self.expandEnumerants = expandEnumerants
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -060099 self.valid_usage_path = valid_usage_path
Mark Lobodzinski85672672016-10-13 08:36:42 -0600100
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600101# ParameterValidationOutputGenerator - subclass of OutputGenerator.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600102# 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)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600117class ParameterValidationOutputGenerator(OutputGenerator):
118 """Generate Parameter Validation code based on XML element attributes"""
Mark Lobodzinski85672672016-10-13 08:36:42 -0600119 # 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
Mark Lobodzinskib6b8bbd2017-02-08 14:37:15 -0700127 self.declarations = []
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700128
129 inline_custom_source_preamble = """
130"""
131
Jeff Bolz7e7e6e02019-01-11 22:53:41 -0600132 # These functions have additional, custom-written checks in the utils cpp file. CodeGen will automatically add a call
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700133 # 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'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700139 'vkCreateRenderPass',
140 'vkCreateRenderPass2KHR',
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700141 'vkCreateBuffer',
142 'vkCreateImage',
143 'vkCreateImageView',
144 'vkCreateGraphicsPipelines',
145 'vkCreateComputePipelines',
Peter Chen85366392019-05-14 15:20:11 -0400146 "vkCreateRayTracingPipelinesNV",
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700147 'vkCreateSampler',
148 'vkCreateDescriptorSetLayout',
149 'vkFreeDescriptorSets',
150 'vkUpdateDescriptorSets',
151 'vkCreateRenderPass',
152 'vkCreateRenderPass2KHR',
153 'vkBeginCommandBuffer',
154 'vkCmdSetViewport',
155 'vkCmdSetScissor',
156 'vkCmdSetLineWidth',
157 'vkCmdDraw',
158 'vkCmdDrawIndirect',
159 'vkCmdDrawIndexedIndirect',
160 'vkCmdCopyImage',
161 'vkCmdBlitImage',
162 'vkCmdCopyBufferToImage',
163 'vkCmdCopyImageToBuffer',
164 'vkCmdUpdateBuffer',
165 'vkCmdFillBuffer',
166 'vkCreateSwapchainKHR',
167 'vkQueuePresentKHR',
168 'vkCreateDescriptorPool',
169 'vkCmdDispatch',
170 'vkCmdDispatchIndirect',
171 'vkCmdDispatchBaseKHR',
172 'vkCmdSetExclusiveScissorNV',
173 'vkCmdSetViewportShadingRatePaletteNV',
174 'vkCmdSetCoarseSampleOrderNV',
175 'vkCmdDrawMeshTasksNV',
176 'vkCmdDrawMeshTasksIndirectNV',
177 'vkCmdDrawMeshTasksIndirectCountNV',
Jeff Bolz7e7e6e02019-01-11 22:53:41 -0600178 'vkAllocateMemory',
Ricardo Garciaa4935972019-02-21 17:43:18 +0100179 'vkCreateAccelerationStructureNV',
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700180 ]
181
Mark Lobodzinski85672672016-10-13 08:36:42 -0600182 # Commands to ignore
183 self.blacklist = [
184 'vkGetInstanceProcAddr',
185 'vkGetDeviceProcAddr',
Mark Young6ba8abe2017-11-09 10:37:04 -0700186 'vkEnumerateInstanceVersion',
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600187 'vkEnumerateInstanceLayerProperties',
188 'vkEnumerateInstanceExtensionProperties',
189 'vkEnumerateDeviceLayerProperties',
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600190 'vkEnumerateDeviceExtensionProperties',
Mike Schuchardt21638df2019-03-16 10:52:02 -0700191 'vkGetDeviceGroupSurfacePresentModes2EXT'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600192 ]
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700193
Dustin Gravesce68f082017-03-30 15:42:16 -0600194 # Structure fields to ignore
195 self.structMemberBlacklist = { 'VkWriteDescriptorSet' : ['dstSet'] }
Mark Lobodzinski85672672016-10-13 08:36:42 -0600196 # Validation conditions for some special case struct members that are conditionally validated
197 self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
198 # Header version
199 self.headerVersion = None
200 # Internal state - accumulators for different inner block text
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600201 self.validation = [] # Text comprising the main per-api parameter validation routines
Mark Lobodzinski85672672016-10-13 08:36:42 -0600202 self.stypes = [] # Values from the VkStructureType enumeration
203 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
204 self.handleTypes = set() # Set of handle type names
205 self.commands = [] # List of CommandData records for all Vulkan commands
206 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
207 self.validatedStructs = dict() # Map of structs type names to generated validation code for that struct type
208 self.enumRanges = dict() # Map of enum name to BEGIN/END range values
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600209 self.enumValueLists = '' # String containing enumerated type map definitions
Mark Lobodzinski85672672016-10-13 08:36:42 -0600210 self.flags = set() # Map of flags typenames
211 self.flagBits = dict() # Map of flag bits typename to list of values
Chris Forbes78ea32d2016-11-28 11:14:17 +1300212 self.newFlags = set() # Map of flags typenames /defined in the current feature/
Mike Schuchardtafd00482017-08-24 15:15:02 -0600213 self.required_extensions = dict() # Dictionary of required extensions for each item in the current extension
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600214 self.extension_type = '' # Type of active feature (extension), device or instance
215 self.extension_names = dict() # Dictionary of extension names to extension name defines
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600216 self.structextends_list = [] # List of extensions which extend another struct
217 self.struct_feature_protect = dict() # Dictionary of structnames and FeatureExtraProtect strings
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600218 self.valid_vuids = set() # Set of all valid VUIDs
Dave Houlton413a6782018-05-22 13:01:54 -0600219 self.vuid_dict = dict() # VUID dictionary (from JSON)
220 self.alias_dict = dict() # Dict of cmd|struct aliases
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700221 self.header_file = False # Header file generation flag
222 self.source_file = False # Source file generation flag
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600223 self.returnedonly_structs = []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600224 # Named tuples to store struct and command data
Mark Lobodzinski85672672016-10-13 08:36:42 -0600225 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600226 'isconst', 'isoptional', 'iscount', 'noautovalidity',
227 'len', 'extstructs', 'condition', 'cdecl'])
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600228 self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl', 'extension_type', 'result'])
Mark Lobodzinski85672672016-10-13 08:36:42 -0600229 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600230
Mark Lobodzinski85672672016-10-13 08:36:42 -0600231 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600232 # Generate Copyright comment block for file
233 def GenerateCopyright(self):
234 copyright = '/* *** THIS FILE IS GENERATED - DO NOT EDIT! ***\n'
235 copyright += ' * See parameter_validation_generator.py for modifications\n'
236 copyright += ' *\n'
Dave Houlton413a6782018-05-22 13:01:54 -0600237 copyright += ' * Copyright (c) 2015-2018 The Khronos Group Inc.\n'
238 copyright += ' * Copyright (c) 2015-2018 LunarG, Inc.\n'
239 copyright += ' * Copyright (C) 2015-2018 Google Inc.\n'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600240 copyright += ' *\n'
241 copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
242 copyright += ' * you may not use this file except in compliance with the License.\n'
243 copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n'
244 copyright += ' * You may obtain a copy of the License at\n'
245 copyright += ' *\n'
246 copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n'
247 copyright += ' *\n'
248 copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
249 copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
250 copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
251 copyright += ' * See the License for the specific language governing permissions and\n'
252 copyright += ' * limitations under the License.\n'
253 copyright += ' *\n'
254 copyright += ' * Author: Mark Lobodzinski <mark@LunarG.com>\n'
Dave Houlton413a6782018-05-22 13:01:54 -0600255 copyright += ' * Author: Dave Houlton <daveh@LunarG.com>\n'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600256 copyright += ' */\n\n'
257 return copyright
258 #
259 # Increases the global indent variable
Mark Lobodzinski85672672016-10-13 08:36:42 -0600260 def incIndent(self, indent):
261 inc = ' ' * self.INDENT_SPACES
262 if indent:
263 return indent + inc
264 return inc
265 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600266 # Decreases the global indent variable
Mark Lobodzinski85672672016-10-13 08:36:42 -0600267 def decIndent(self, indent):
268 if indent and (len(indent) > self.INDENT_SPACES):
269 return indent[:-self.INDENT_SPACES]
270 return ''
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600271 #
Dave Houlton413a6782018-05-22 13:01:54 -0600272 # Walk the JSON-derived dict and find all "vuid" key values
273 def ExtractVUIDs(self, d):
274 if hasattr(d, 'items'):
275 for k, v in d.items():
276 if k == "vuid":
277 yield v
278 elif isinstance(v, dict):
279 for s in self.ExtractVUIDs(v):
280 yield s
281 elif isinstance (v, list):
282 for l in v:
283 for s in self.ExtractVUIDs(l):
284 yield s
Mark Lobodzinski85672672016-10-13 08:36:42 -0600285 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600286 # Called at file creation time
Mark Lobodzinski85672672016-10-13 08:36:42 -0600287 def beginFile(self, genOpts):
288 OutputGenerator.beginFile(self, genOpts)
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700289 self.header_file = (genOpts.filename == 'parameter_validation.h')
290 self.source_file = (genOpts.filename == 'parameter_validation.cpp')
291
292 if not self.header_file and not self.source_file:
293 print("Error: Output Filenames have changed, update generator source.\n")
294 sys.exit(1)
295
296 if self.source_file or self.header_file:
297 # Output Copyright text
298 s = self.GenerateCopyright()
299 write(s, file=self.outFile)
300
301 if self.header_file:
302 return
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -0600303
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700304 # Build map of structure type names to VkStructureType enum values
305 # Find all types of category "struct"
306 for struct in self.registry.tree.iterfind('types/type[@category="struct"]'):
307 # Check if struct has member named "sType" of type "VkStructureType" which has values defined
308 stype = struct.find('member[name="sType"][type="VkStructureType"][@values]')
Shahbaz Youssefi23aee922019-01-11 14:04:49 -0500309 if stype is not None:
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700310 # Store VkStructureType value for this type
311 self.structTypes[struct.get('name')] = stype.get('values')
312
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -0600313 self.valid_usage_path = genOpts.valid_usage_path
314 vu_json_filename = os.path.join(self.valid_usage_path + os.sep, 'validusage.json')
315 if os.path.isfile(vu_json_filename):
316 json_file = open(vu_json_filename, 'r')
317 self.vuid_dict = json.load(json_file)
318 json_file.close()
319 if len(self.vuid_dict) == 0:
320 print("Error: Could not find, or error loading %s/validusage.json\n", vu_json_filename)
321 sys.exit(1)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600322 #
Dave Houlton413a6782018-05-22 13:01:54 -0600323 # Build a set of all vuid text strings found in validusage.json
324 for json_vuid_string in self.ExtractVUIDs(self.vuid_dict):
325 self.valid_vuids.add(json_vuid_string)
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600326 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600327 # Headers
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700328 write('#include "chassis.h"', file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600329 self.newline()
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700330 write('#include "stateless_validation.h"', file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600331 self.newline()
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600332 #
333 # Called at end-time for final content output
Mark Lobodzinski85672672016-10-13 08:36:42 -0600334 def endFile(self):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700335 if self.source_file:
336 # C-specific
337 self.newline()
338 write(self.enumValueLists, file=self.outFile)
339 self.newline()
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600340
Locke6b6b7382019-04-16 15:08:49 -0600341 pnext_handler = 'bool StatelessValidation::ValidatePnextStructContents(const char *api_name, const ParameterName &parameter_name, const VkBaseOutStructure* header) {\n'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700342 pnext_handler += ' bool skip = false;\n'
343 pnext_handler += ' switch(header->sType) {\n'
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600344
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700345 # Do some processing here to extract data from validatedstructs...
346 for item in self.structextends_list:
347 postProcSpec = {}
348 postProcSpec['ppp'] = '' if not item else '{postProcPrefix}'
349 postProcSpec['pps'] = '' if not item else '{postProcSuffix}'
350 postProcSpec['ppi'] = '' if not item else '{postProcInsert}'
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600351
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700352 pnext_case = '\n'
353 protect = ''
354 # Guard struct cases with feature ifdefs, if necessary
355 if item in self.struct_feature_protect.keys():
356 protect = self.struct_feature_protect[item]
357 pnext_case += '#ifdef %s\n' % protect
358 pnext_case += ' // Validation code for %s structure members\n' % item
359 pnext_case += ' case %s: {\n' % self.structTypes[item]
360 pnext_case += ' %s *structure = (%s *) header;\n' % (item, item)
361 expr = self.expandStructCode(item, item, 'structure->', '', ' ', [], postProcSpec)
362 struct_validation_source = self.ScrubStructCode(expr)
363 pnext_case += '%s' % struct_validation_source
364 pnext_case += ' } break;\n'
Raul Tambre7b300182019-05-04 11:25:14 +0300365 if protect:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700366 pnext_case += '#endif // %s\n' % protect
367 # Skip functions containing no validation
Raul Tambre7b300182019-05-04 11:25:14 +0300368 if struct_validation_source:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700369 pnext_handler += pnext_case;
370 pnext_handler += ' default:\n'
371 pnext_handler += ' skip = false;\n'
372 pnext_handler += ' }\n'
373 pnext_handler += ' return skip;\n'
374 pnext_handler += '}\n'
375 write(pnext_handler, file=self.outFile)
376 self.newline()
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600377
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700378 ext_template = 'bool StatelessValidation::OutputExtensionError(const std::string &api_name, const std::string &extension_name) {\n'
379 ext_template += ' return log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,\n'
380 ext_template += ' kVUID_PVError_ExtensionNotEnabled, "Attemped to call %s() but its required extension %s has not been enabled\\n",\n'
381 ext_template += ' api_name.c_str(), extension_name.c_str());\n'
382 ext_template += '}\n'
383 write(ext_template, file=self.outFile)
384 self.newline()
385 commands_text = '\n'.join(self.validation)
386 write(commands_text, file=self.outFile)
387 self.newline()
388 if self.header_file:
389 # Output declarations and record intercepted procedures
390 write('\n'.join(self.declarations), file=self.outFile)
391 # Finish processing in superclass
392 OutputGenerator.endFile(self)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600393 #
394 # Processing at beginning of each feature or extension
Mark Lobodzinski85672672016-10-13 08:36:42 -0600395 def beginFeature(self, interface, emit):
396 # Start processing in superclass
397 OutputGenerator.beginFeature(self, interface, emit)
398 # C-specific
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600399 # Accumulate includes, defines, types, enums, function pointer typedefs, end function prototypes separately for this
400 # feature. They're only printed in endFeature().
Mark Lobodzinski85672672016-10-13 08:36:42 -0600401 self.headerVersion = None
Mark Lobodzinski85672672016-10-13 08:36:42 -0600402 self.stypes = []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600403 self.commands = []
404 self.structMembers = []
Chris Forbes78ea32d2016-11-28 11:14:17 +1300405 self.newFlags = set()
Mark Lobodzinski62f71562017-10-24 13:41:18 -0600406 self.featureExtraProtect = GetFeatureProtect(interface)
Mike Schuchardtafd00482017-08-24 15:15:02 -0600407 # Get base list of extension dependencies for all items in this extension
408 base_required_extensions = []
Mark Lobodzinski31964ca2017-09-18 14:15:09 -0600409 if "VK_VERSION_1" not in self.featureName:
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600410 # Save Name Define to get correct enable name later
411 nameElem = interface[0][1]
412 name = nameElem.get('name')
413 self.extension_names[self.featureName] = name
414 # This extension is the first dependency for this command
Mike Schuchardtafd00482017-08-24 15:15:02 -0600415 base_required_extensions.append(self.featureName)
416 # Add any defined extension dependencies to the base dependency list for this extension
417 requires = interface.get('requires')
418 if requires is not None:
419 base_required_extensions.extend(requires.split(','))
Mike Schuchardtafd00482017-08-24 15:15:02 -0600420 # Build dictionary of extension dependencies for each item in this extension
421 self.required_extensions = dict()
422 for require_element in interface.findall('require'):
423 # Copy base extension dependency list
424 required_extensions = list(base_required_extensions)
425 # Add any additional extension dependencies specified in this require block
426 additional_extensions = require_element.get('extension')
427 if additional_extensions:
428 required_extensions.extend(additional_extensions.split(','))
429 # Save full extension list for all named items
430 for element in require_element.findall('*[@name]'):
431 self.required_extensions[element.get('name')] = required_extensions
432
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600433 # And note if this is an Instance or Device extension
434 self.extension_type = interface.get('type')
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600435 #
436 # Called at the end of each extension (feature)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600437 def endFeature(self):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700438 if self.header_file:
439 return
Mark Lobodzinski85672672016-10-13 08:36:42 -0600440 # C-specific
441 # Actually write the interface to the output file.
442 if (self.emit):
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600443 # If type declarations are needed by other features based on this one, it may be necessary to suppress the ExtraProtect,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600444 # or move it below the 'for section...' loop.
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600445 ifdef = ''
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100446 if (self.featureExtraProtect is not None):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600447 ifdef = '#ifdef %s\n' % self.featureExtraProtect
448 self.validation.append(ifdef)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600449 # Generate the struct member checking code from the captured data
450 self.processStructMemberData()
451 # Generate the command parameter checking code from the captured data
452 self.processCmdData()
453 # Write the declaration for the HeaderVersion
454 if self.headerVersion:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700455 write('const uint32_t GeneratedVulkanHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600456 self.newline()
457 # Write the declarations for the VkFlags values combining all flag bits
Chris Forbes78ea32d2016-11-28 11:14:17 +1300458 for flag in sorted(self.newFlags):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600459 flagBits = flag.replace('Flags', 'FlagBits')
460 if flagBits in self.flagBits:
461 bits = self.flagBits[flagBits]
462 decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
463 for bit in bits[1:]:
464 decl += '|' + bit
465 decl += ';'
466 write(decl, file=self.outFile)
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600467 endif = '\n'
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100468 if (self.featureExtraProtect is not None):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600469 endif = '#endif // %s\n' % self.featureExtraProtect
470 self.validation.append(endif)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600471 # Finish processing in superclass
472 OutputGenerator.endFeature(self)
473 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600474 # Type generation
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700475 def genType(self, typeinfo, name, alias):
Dave Houlton413a6782018-05-22 13:01:54 -0600476 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100477 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600478 self.alias_dict[name]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700479 OutputGenerator.genType(self, typeinfo, name, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600480 typeElem = typeinfo.elem
Mark Lobodzinski87017df2018-05-30 11:29:24 -0600481 # If the type is a struct type, traverse the embedded <member> tags generating a structure. Otherwise, emit the tag text.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600482 category = typeElem.get('category')
483 if (category == 'struct' or category == 'union'):
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700484 self.genStruct(typeinfo, name, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600485 elif (category == 'handle'):
486 self.handleTypes.add(name)
487 elif (category == 'bitmask'):
488 self.flags.add(name)
Chris Forbes78ea32d2016-11-28 11:14:17 +1300489 self.newFlags.add(name)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600490 elif (category == 'define'):
491 if name == 'VK_HEADER_VERSION':
492 nameElem = typeElem.find('name')
493 self.headerVersion = noneStr(nameElem.tail).strip()
494 #
495 # Struct parameter check generation.
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600496 # This is a special case of the <type> tag where the contents are interpreted as a set of <member> tags instead of freeform C
497 # type declarations. The <member> tags are just like <param> tags - they are a declaration of a struct or union member.
498 # Only simple member declarations are supported (no nested structs etc.)
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700499 def genStruct(self, typeinfo, typeName, alias):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700500 if not self.source_file:
501 return
Dave Houlton413a6782018-05-22 13:01:54 -0600502 # alias has already been recorded in genType, above
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700503 OutputGenerator.genStruct(self, typeinfo, typeName, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600504 conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
505 members = typeinfo.elem.findall('.//member')
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600506 if self.featureExtraProtect is not None:
507 self.struct_feature_protect[typeName] = self.featureExtraProtect
Mark Lobodzinski85672672016-10-13 08:36:42 -0600508 #
509 # Iterate over members once to get length parameters for arrays
510 lens = set()
511 for member in members:
512 len = self.getLen(member)
513 if len:
514 lens.add(len)
515 #
516 # Generate member info
517 membersInfo = []
518 for member in members:
519 # Get the member's type and name
520 info = self.getTypeNameTuple(member)
521 type = info[0]
522 name = info[1]
523 stypeValue = ''
524 cdecl = self.makeCParamDecl(member, 0)
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700525
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600526 # Store pointer/array/string info -- Check for parameter name in lens set
Mark Lobodzinski85672672016-10-13 08:36:42 -0600527 iscount = False
528 if name in lens:
529 iscount = True
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600530 # The pNext members are not tagged as optional, but are treated as optional for parameter NULL checks. Static array
531 # members are also treated as optional to skip NULL pointer validation, as they won't be NULL.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600532 isstaticarray = self.paramIsStaticArray(member)
533 isoptional = False
534 if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
535 isoptional = True
Dustin Gravesce68f082017-03-30 15:42:16 -0600536 # Determine if value should be ignored by code generation.
537 noautovalidity = False
538 if (member.attrib.get('noautovalidity') is not None) or ((typeName in self.structMemberBlacklist) and (name in self.structMemberBlacklist[typeName])):
539 noautovalidity = True
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600540 structextends = False
Mark Lobodzinski85672672016-10-13 08:36:42 -0600541 membersInfo.append(self.CommandParam(type=type, name=name,
542 ispointer=self.paramIsPointer(member),
543 isstaticarray=isstaticarray,
544 isbool=True if type == 'VkBool32' else False,
545 israngedenum=True if type in self.enumRanges else False,
546 isconst=True if 'const' in cdecl else False,
547 isoptional=isoptional,
548 iscount=iscount,
Dustin Gravesce68f082017-03-30 15:42:16 -0600549 noautovalidity=noautovalidity,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600550 len=self.getLen(member),
Mike Schuchardta40d0b02017-07-23 12:47:47 -0600551 extstructs=self.registry.validextensionstructs[typeName] if name == 'pNext' else None,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600552 condition=conditions[name] if conditions and name in conditions else None,
553 cdecl=cdecl))
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600554 # If this struct extends another, keep its name in list for further processing
555 if typeinfo.elem.attrib.get('structextends') is not None:
556 self.structextends_list.append(typeName)
557 # Returnedonly structs should have most of their members ignored -- on entry, we only care about validating the sType and
558 # pNext members. Everything else will be overwritten by the callee.
559 if typeinfo.elem.attrib.get('returnedonly') is not None:
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600560 self.returnedonly_structs.append(typeName)
561 membersInfo = [m for m in membersInfo if m.name in ('sType', 'pNext')]
Mark Lobodzinski85672672016-10-13 08:36:42 -0600562 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
563 #
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600564 # Capture group (e.g. C "enum" type) info to be used for param check code generation.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600565 # These are concatenated together with other types.
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700566 def genGroup(self, groupinfo, groupName, alias):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700567 if not self.source_file:
568 return
Dave Houlton413a6782018-05-22 13:01:54 -0600569 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100570 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600571 self.alias_dict[groupName]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700572 OutputGenerator.genGroup(self, groupinfo, groupName, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600573 groupElem = groupinfo.elem
Mark Lobodzinski85672672016-10-13 08:36:42 -0600574 # Store the sType values
575 if groupName == 'VkStructureType':
576 for elem in groupElem.findall('enum'):
577 self.stypes.append(elem.get('name'))
578 elif 'FlagBits' in groupName:
579 bits = []
580 for elem in groupElem.findall('enum'):
Shannon McPherson533a66c2018-08-21 12:09:25 -0600581 if elem.get('supported') != 'disabled':
582 bits.append(elem.get('name'))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600583 if bits:
584 self.flagBits[groupName] = bits
585 else:
586 # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
587 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
588 expandPrefix = expandName
589 expandSuffix = ''
590 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
591 if expandSuffixMatch:
592 expandSuffix = '_' + expandSuffixMatch.group()
593 # Strip off the suffix from the prefix
594 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
595 isEnum = ('FLAG_BITS' not in expandPrefix)
596 if isEnum:
597 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600598 # Create definition for a list containing valid enum values for this enumerated type
Mike Schuchardt21638df2019-03-16 10:52:02 -0700599 if self.featureExtraProtect is not None:
600 enum_entry = '\n#ifdef %s\n' % self.featureExtraProtect
601 else:
602 enum_entry = ''
603 enum_entry += 'const std::vector<%s> All%sEnums = {' % (groupName, groupName)
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600604 for enum in groupElem:
605 name = enum.get('name')
Mark Lobodzinski117d88f2017-07-27 12:09:08 -0600606 if name is not None and enum.get('supported') != 'disabled':
607 enum_entry += '%s, ' % name
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600608 enum_entry += '};\n'
Mike Schuchardt21638df2019-03-16 10:52:02 -0700609 if self.featureExtraProtect is not None:
610 enum_entry += '#endif // %s' % self.featureExtraProtect
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600611 self.enumValueLists += enum_entry
Mark Lobodzinski85672672016-10-13 08:36:42 -0600612 #
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600613 # Capture command parameter info to be used for param check code generation.
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700614 def genCmd(self, cmdinfo, name, alias):
Dave Houlton413a6782018-05-22 13:01:54 -0600615 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100616 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600617 self.alias_dict[name]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700618 OutputGenerator.genCmd(self, cmdinfo, name, alias)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600619 decls = self.makeCDecls(cmdinfo.elem)
620 typedef = decls[1]
621 typedef = typedef.split(')',1)[1]
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700622 if self.header_file:
623 if name not in self.blacklist:
624 if (self.featureExtraProtect is not None):
625 self.declarations += [ '#ifdef %s' % self.featureExtraProtect ]
626 # Strip off 'vk' from API name
627 self.declarations += [ '%s%s' % ('bool PreCallValidate', decls[0].split("VKAPI_CALL vk")[1])]
628 if (self.featureExtraProtect is not None):
629 self.declarations += [ '#endif' ]
630 if self.source_file:
631 if name not in self.blacklist:
632 params = cmdinfo.elem.findall('param')
633 # Get list of array lengths
634 lens = set()
635 for param in params:
636 len = self.getLen(param)
637 if len:
638 lens.add(len)
639 # Get param info
640 paramsInfo = []
641 for param in params:
642 paramInfo = self.getTypeNameTuple(param)
643 cdecl = self.makeCParamDecl(param, 0)
644 # Check for parameter name in lens set
645 iscount = False
646 if paramInfo[1] in lens:
647 iscount = True
648 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
649 ispointer=self.paramIsPointer(param),
650 isstaticarray=self.paramIsStaticArray(param),
651 isbool=True if paramInfo[0] == 'VkBool32' else False,
652 israngedenum=True if paramInfo[0] in self.enumRanges else False,
653 isconst=True if 'const' in cdecl else False,
654 isoptional=self.paramIsOptional(param),
655 iscount=iscount,
656 noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
657 len=self.getLen(param),
658 extstructs=None,
659 condition=None,
660 cdecl=cdecl))
661 # Save return value information, if any
662 result_type = ''
663 resultinfo = cmdinfo.elem.find('proto/type')
664 if (resultinfo is not None and resultinfo.text != 'void'):
665 result_type = resultinfo.text
666 self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0], extension_type=self.extension_type, result=result_type))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600667 #
668 # Check if the parameter passed in is a pointer
669 def paramIsPointer(self, param):
670 ispointer = 0
671 paramtype = param.find('type')
672 if (paramtype.tail is not None) and ('*' in paramtype.tail):
673 ispointer = paramtype.tail.count('*')
674 elif paramtype.text[:4] == 'PFN_':
675 # Treat function pointer typedefs as a pointer to a single value
676 ispointer = 1
677 return ispointer
678 #
679 # Check if the parameter passed in is a static array
680 def paramIsStaticArray(self, param):
681 isstaticarray = 0
682 paramname = param.find('name')
683 if (paramname.tail is not None) and ('[' in paramname.tail):
684 isstaticarray = paramname.tail.count('[')
685 return isstaticarray
686 #
687 # Check if the parameter passed in is optional
688 # Returns a list of Boolean values for comma separated len attributes (len='false,true')
689 def paramIsOptional(self, param):
690 # See if the handle is optional
691 isoptional = False
692 # Simple, if it's optional, return true
693 optString = param.attrib.get('optional')
694 if optString:
695 if optString == 'true':
696 isoptional = True
697 elif ',' in optString:
698 opts = []
699 for opt in optString.split(','):
700 val = opt.strip()
701 if val == 'true':
702 opts.append(True)
703 elif val == 'false':
704 opts.append(False)
705 else:
706 print('Unrecognized len attribute value',val)
707 isoptional = opts
708 return isoptional
709 #
710 # Check if the handle passed in is optional
711 # Uses the same logic as ValidityOutputGenerator.isHandleOptional
712 def isHandleOptional(self, param, lenParam):
713 # Simple, if it's optional, return true
714 if param.isoptional:
715 return True
716 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
717 if param.noautovalidity:
718 return True
719 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
720 if lenParam and lenParam.isoptional:
721 return True
722 return False
723 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600724 # Retrieve the value of the len tag
725 def getLen(self, param):
726 result = None
727 len = param.attrib.get('len')
728 if len and len != 'null-terminated':
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600729 # For string arrays, 'len' can look like 'count,null-terminated', indicating that we have a null terminated array of
730 # strings. We strip the null-terminated from the 'len' field and only return the parameter specifying the string count
Mark Lobodzinski85672672016-10-13 08:36:42 -0600731 if 'null-terminated' in len:
732 result = len.split(',')[0]
733 else:
734 result = len
735 result = str(result).replace('::', '->')
736 return result
737 #
738 # Retrieve the type and name for a parameter
739 def getTypeNameTuple(self, param):
740 type = ''
741 name = ''
742 for elem in param:
743 if elem.tag == 'type':
744 type = noneStr(elem.text)
745 elif elem.tag == 'name':
746 name = noneStr(elem.text)
747 return (type, name)
748 #
749 # Find a named parameter in a parameter list
750 def getParamByName(self, params, name):
751 for param in params:
752 if param.name == name:
753 return param
754 return None
755 #
756 # Extract length values from latexmath. Currently an inflexible solution that looks for specific
757 # patterns that are found in vk.xml. Will need to be updated when new patterns are introduced.
758 def parseLateXMath(self, source):
759 name = 'ERROR'
760 decoratedName = 'ERROR'
761 if 'mathit' in source:
Mark Lobodzinski36c33862017-02-13 10:15:53 -0700762 # Matches expressions similar to 'latexmath:[\lceil{\mathit{rasterizationSamples} \over 32}\rceil]'
763 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)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600764 if not match or match.group(1) != match.group(4):
765 raise 'Unrecognized latexmath expression'
766 name = match.group(2)
767 decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
768 else:
Mark Lobodzinski36c33862017-02-13 10:15:53 -0700769 # Matches expressions similar to 'latexmath : [dataSize \over 4]'
Shannon McPhersonbd68df02018-10-29 15:04:41 -0600770 match = re.match(r'latexmath\s*\:\s*\[\s*(\\textrm\{)?(\w+)\}?\s*\\over\s*(\d+)\s*\]', source)
771 name = match.group(2)
772 decoratedName = '{}/{}'.format(*match.group(2, 3))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600773 return name, decoratedName
774 #
775 # Get the length paramater record for the specified parameter name
776 def getLenParam(self, params, name):
777 lenParam = None
778 if name:
779 if '->' in name:
780 # The count is obtained by dereferencing a member of a struct parameter
781 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600782 isstaticarray=None, isoptional=False, type=None, noautovalidity=False,
783 len=None, extstructs=None, condition=None, cdecl=None)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600784 elif 'latexmath' in name:
785 lenName, decoratedName = self.parseLateXMath(name)
786 lenParam = self.getParamByName(params, lenName)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600787 else:
788 lenParam = self.getParamByName(params, name)
789 return lenParam
790 #
791 # Convert a vulkan.h command declaration into a parameter_validation.h definition
792 def getCmdDef(self, cmd):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600793 # Strip the trailing ';' and split into individual lines
794 lines = cmd.cdecl[:-1].split('\n')
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600795 cmd_hdr = '\n'.join(lines)
796 return cmd_hdr
Mark Lobodzinski85672672016-10-13 08:36:42 -0600797 #
798 # Generate the code to check for a NULL dereference before calling the
799 # validation function
800 def genCheckedLengthCall(self, name, exprs):
801 count = name.count('->')
802 if count:
803 checkedExpr = []
804 localIndent = ''
805 elements = name.split('->')
806 # Open the if expression blocks
807 for i in range(0, count):
808 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
809 localIndent = self.incIndent(localIndent)
810 # Add the validation expression
811 for expr in exprs:
812 checkedExpr.append(localIndent + expr)
813 # Close the if blocks
814 for i in range(0, count):
815 localIndent = self.decIndent(localIndent)
816 checkedExpr.append(localIndent + '}\n')
817 return [checkedExpr]
818 # No if statements were required
819 return exprs
820 #
821 # Generate code to check for a specific condition before executing validation code
822 def genConditionalCall(self, prefix, condition, exprs):
823 checkedExpr = []
824 localIndent = ''
825 formattedCondition = condition.format(prefix)
826 checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
827 checkedExpr.append(localIndent + '{\n')
828 localIndent = self.incIndent(localIndent)
829 for expr in exprs:
830 checkedExpr.append(localIndent + expr)
831 localIndent = self.decIndent(localIndent)
832 checkedExpr.append(localIndent + '}\n')
833 return [checkedExpr]
834 #
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600835 # Get VUID identifier from implicit VUID tag
Dave Houlton413a6782018-05-22 13:01:54 -0600836 def GetVuid(self, name, suffix):
837 vuid_string = 'VUID-%s-%s' % (name, suffix)
838 vuid = "kVUIDUndefined"
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600839 if '->' in vuid_string:
Dave Houlton413a6782018-05-22 13:01:54 -0600840 return vuid
841 if vuid_string in self.valid_vuids:
842 vuid = "\"%s\"" % vuid_string
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600843 else:
Dave Houlton413a6782018-05-22 13:01:54 -0600844 if name in self.alias_dict:
845 alias_string = 'VUID-%s-%s' % (self.alias_dict[name], suffix)
846 if alias_string in self.valid_vuids:
Shannon McPherson23d97212019-02-18 13:39:42 -0700847 vuid = "\"%s\"" % alias_string
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600848 return vuid
849 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600850 # Generate the sType check string
Mark Lobodzinski9cf24dd2017-06-28 14:23:22 -0600851 def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600852 checkExpr = []
853 stype = self.structTypes[value.type]
Mark Lobodzinski59603552018-05-29 16:14:59 -0600854 vuid_name = struct_type_name if struct_type_name is not None else funcPrintName
855 stype_vuid = self.GetVuid(value.type, "sType-sType")
856 param_vuid = self.GetVuid(vuid_name, "%s-parameter" % value.name)
857
Mark Lobodzinski85672672016-10-13 08:36:42 -0600858 if lenValue:
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800859 count_required_vuid = self.GetVuid(vuid_name, "%s-arraylength" % lenValue.name)
860
Mark Lobodzinski85672672016-10-13 08:36:42 -0600861 # This is an array with a pointer to a count value
862 if lenValue.ispointer:
863 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800864 checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {}, {});\n'.format(
865 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))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600866 # This is an array with an integer count value
867 else:
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800868 checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {});\n'.format(
869 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))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600870 # This is an individual struct
871 else:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700872 checkExpr.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {}, {}, {});\n'.format(
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700873 funcPrintName, valuePrintName, prefix, valueRequired, param_vuid, stype_vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600874 return checkExpr
875 #
876 # Generate the handle check string
877 def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
878 checkExpr = []
879 if lenValue:
880 if lenValue.ispointer:
881 # This is assumed to be an output array with a pointer to a count value
882 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
883 else:
884 # This is an array with an integer count value
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700885 checkExpr.append('skip |= validate_handle_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format(
Mark Lobodzinski85672672016-10-13 08:36:42 -0600886 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
887 else:
888 # This is assumed to be an output handle pointer
889 raise('Unsupported parameter validation case: Output handles are not NULL checked')
890 return checkExpr
891 #
892 # Generate check string for an array of VkFlags values
893 def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
894 checkExpr = []
895 flagBitsName = value.type.replace('Flags', 'FlagBits')
896 if not flagBitsName in self.flagBits:
897 raise('Unsupported parameter validation case: array of reserved VkFlags')
898 else:
899 allFlags = 'All' + flagBitsName
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700900 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))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600901 return checkExpr
902 #
903 # Generate pNext check string
Mark Lobodzinski3c828522017-06-26 13:05:57 -0600904 def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600905 checkExpr = []
906 # Generate an array of acceptable VkStructureType values for pNext
907 extStructCount = 0
908 extStructVar = 'NULL'
909 extStructNames = 'NULL'
Dave Houlton413a6782018-05-22 13:01:54 -0600910 vuid = self.GetVuid(struct_type_name, "pNext-pNext")
Mark Lobodzinski85672672016-10-13 08:36:42 -0600911 if value.extstructs:
Mike Schuchardtc73d07e2017-07-12 10:10:01 -0600912 extStructVar = 'allowed_structs_{}'.format(struct_type_name)
913 extStructCount = 'ARRAY_SIZE({})'.format(extStructVar)
Mike Schuchardta40d0b02017-07-23 12:47:47 -0600914 extStructNames = '"' + ', '.join(value.extstructs) + '"'
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700915 checkExpr.append('const VkStructureType {}[] = {{ {} }};\n'.format(extStructVar, ', '.join([self.structTypes[s] for s in value.extstructs])))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700916 checkExpr.append('skip |= validate_struct_pnext("{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedVulkanHeaderVersion, {});\n'.format(
Mark Lobodzinski3c828522017-06-26 13:05:57 -0600917 funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, vuid, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600918 return checkExpr
919 #
920 # Generate the pointer check string
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600921 def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600922 checkExpr = []
Mark Lobodzinskidead0b62017-06-28 13:22:03 -0600923 vuid_tag_name = struct_type_name if struct_type_name is not None else funcPrintName
Mark Lobodzinski85672672016-10-13 08:36:42 -0600924 if lenValue:
Dave Houlton413a6782018-05-22 13:01:54 -0600925 count_required_vuid = self.GetVuid(vuid_tag_name, "%s-arraylength" % (lenValue.name))
926 array_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600927 # This is an array with a pointer to a count value
928 if lenValue.ispointer:
929 # If count and array parameters are optional, there will be no validation
930 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
931 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700932 checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600933 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600934 # This is an array with an integer count value
935 else:
936 # If count and array parameters are optional, there will be no validation
937 if valueRequired == 'true' or lenValueRequired == 'true':
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600938 if value.type != 'char':
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700939 checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600940 funcPrintName, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
941 else:
942 # Arrays of strings receive special processing
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700943 checkExpr.append('skip |= validate_string_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, {pf}{vn}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600944 funcPrintName, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600945 if checkExpr:
946 if lenValue and ('->' in lenValue.name):
947 # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
948 checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
949 # This is an individual struct that is not allowed to be NULL
950 elif not value.isoptional:
951 # Function pointers need a reinterpret_cast to void*
Dave Houlton413a6782018-05-22 13:01:54 -0600952 ptr_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600953 if value.type[:4] == 'PFN_':
Dave Houlton413a6782018-05-22 13:01:54 -0600954 allocator_dict = {'pfnAllocation': '"VUID-VkAllocationCallbacks-pfnAllocation-00632"',
955 'pfnReallocation': '"VUID-VkAllocationCallbacks-pfnReallocation-00633"',
956 'pfnFree': '"VUID-VkAllocationCallbacks-pfnFree-00634"',
Mark Lobodzinski02fa1972017-06-28 14:46:14 -0600957 }
958 vuid = allocator_dict.get(value.name)
959 if vuid is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600960 ptr_required_vuid = vuid
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700961 checkExpr.append('skip |= validate_required_pointer("{}", {ppp}"{}"{pps}, reinterpret_cast<const void*>({}{}), {});\n'.format(funcPrintName, valuePrintName, prefix, value.name, ptr_required_vuid, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600962 else:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700963 checkExpr.append('skip |= validate_required_pointer("{}", {ppp}"{}"{pps}, {}{}, {});\n'.format(funcPrintName, valuePrintName, prefix, value.name, ptr_required_vuid, **postProcSpec))
Ricardo Garcia98ba9642019-02-19 11:25:18 +0100964 else:
965 # Special case for optional internal allocation function pointers.
966 if (value.type, value.name) == ('PFN_vkInternalAllocationNotification', 'pfnInternalAllocation'):
Ricardo Garciac52489a2019-04-02 18:15:20 +0200967 checkExpr.extend(self.internalAllocationCheck(funcPrintName, prefix, value.name, 'pfnInternalFree', postProcSpec))
968 elif (value.type, value.name) == ('PFN_vkInternalFreeNotification', 'pfnInternalFree'):
969 checkExpr.extend(self.internalAllocationCheck(funcPrintName, prefix, value.name, 'pfnInternalAllocation', postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600970 return checkExpr
Ricardo Garciac52489a2019-04-02 18:15:20 +0200971
972 #
973 # Generate internal allocation function pointer check.
974 def internalAllocationCheck(self, funcPrintName, prefix, name, complementaryName, postProcSpec):
975 checkExpr = []
976 vuid = '"VUID-VkAllocationCallbacks-pfnInternalAllocation-00635"'
977 checkExpr.append('if ({}{} != NULL)'.format(prefix, name))
978 checkExpr.append('{')
979 local_indent = self.incIndent('')
980 # Function pointers need a reinterpret_cast to void*
981 checkExpr.append(local_indent + 'skip |= validate_required_pointer("{}", {ppp}"{}{}"{pps}, reinterpret_cast<const void*>({}{}), {});\n'.format(funcPrintName, prefix, complementaryName, prefix, complementaryName, vuid, **postProcSpec))
982 checkExpr.append('}\n')
983 return checkExpr
984
Mark Lobodzinski85672672016-10-13 08:36:42 -0600985 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -0600986 # Process struct member validation code, performing name substitution if required
Mark Lobodzinski85672672016-10-13 08:36:42 -0600987 def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
988 # Build format specifier list
989 kwargs = {}
990 if '{postProcPrefix}' in line:
991 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
992 if type(memberDisplayNamePrefix) is tuple:
993 kwargs['postProcPrefix'] = 'ParameterName('
994 else:
995 kwargs['postProcPrefix'] = postProcSpec['ppp']
996 if '{postProcSuffix}' in line:
997 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
998 if type(memberDisplayNamePrefix) is tuple:
999 kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
1000 else:
1001 kwargs['postProcSuffix'] = postProcSpec['pps']
1002 if '{postProcInsert}' in line:
1003 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
1004 if type(memberDisplayNamePrefix) is tuple:
1005 kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
1006 else:
1007 kwargs['postProcInsert'] = postProcSpec['ppi']
1008 if '{funcName}' in line:
1009 kwargs['funcName'] = funcName
1010 if '{valuePrefix}' in line:
1011 kwargs['valuePrefix'] = memberNamePrefix
1012 if '{displayNamePrefix}' in line:
1013 # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
1014 if type(memberDisplayNamePrefix) is tuple:
1015 kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
1016 else:
1017 kwargs['displayNamePrefix'] = memberDisplayNamePrefix
1018
1019 if kwargs:
1020 # Need to escape the C++ curly braces
1021 if 'IndexVector' in line:
1022 line = line.replace('IndexVector{ ', 'IndexVector{{ ')
1023 line = line.replace(' }),', ' }}),')
1024 return line.format(**kwargs)
1025 return line
1026 #
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001027 # Process struct member validation code, stripping metadata
1028 def ScrubStructCode(self, code):
1029 scrubbed_lines = ''
1030 for line in code:
1031 if 'validate_struct_pnext' in line:
1032 continue
1033 if 'allowed_structs' in line:
1034 continue
1035 if 'xml-driven validation' in line:
1036 continue
1037 line = line.replace('{postProcPrefix}', '')
1038 line = line.replace('{postProcSuffix}', '')
1039 line = line.replace('{postProcInsert}', '')
1040 line = line.replace('{funcName}', '')
1041 line = line.replace('{valuePrefix}', '')
1042 line = line.replace('{displayNamePrefix}', '')
1043 line = line.replace('{IndexVector}', '')
1044 line = line.replace('local_data->', '')
1045 scrubbed_lines += line
1046 return scrubbed_lines
1047 #
Mark Lobodzinski85672672016-10-13 08:36:42 -06001048 # Process struct validation code for inclusion in function or parent struct validation code
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001049 def expandStructCode(self, item_type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
1050 lines = self.validatedStructs[item_type]
Mark Lobodzinski85672672016-10-13 08:36:42 -06001051 for line in lines:
1052 if output:
1053 output[-1] += '\n'
1054 if type(line) is list:
1055 for sub in line:
1056 output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1057 else:
1058 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1059 return output
1060 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001061 # Process struct pointer/array validation code, performing name substitution if required
Mark Lobodzinski85672672016-10-13 08:36:42 -06001062 def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
1063 expr = []
1064 expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
1065 expr.append('{')
1066 indent = self.incIndent(None)
1067 if lenValue:
1068 # Need to process all elements in the array
1069 indexName = lenValue.name.replace('Count', 'Index')
1070 expr[-1] += '\n'
Mark Young39389872017-01-19 21:10:49 -07001071 if lenValue.ispointer:
1072 # If the length value is a pointer, de-reference it for the count.
1073 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < *{}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
1074 else:
1075 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001076 expr.append(indent + '{')
1077 indent = self.incIndent(indent)
1078 # Prefix for value name to display in error message
Mark Lobodzinski6f82eb52016-12-05 07:38:41 -07001079 if value.ispointer == 2:
1080 memberNamePrefix = '{}{}[{}]->'.format(prefix, value.name, indexName)
1081 memberDisplayNamePrefix = ('{}[%i]->'.format(valueDisplayName), indexName)
1082 else:
1083 memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
1084 memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001085 else:
1086 memberNamePrefix = '{}{}->'.format(prefix, value.name)
1087 memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001088 # Expand the struct validation lines
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001089 expr = self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001090 if lenValue:
1091 # Close if and for scopes
1092 indent = self.decIndent(indent)
1093 expr.append(indent + '}\n')
1094 expr.append('}\n')
1095 return expr
1096 #
1097 # Generate the parameter checking code
1098 def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
1099 lines = [] # Generated lines of code
1100 unused = [] # Unused variable names
1101 for value in values:
1102 usedLines = []
1103 lenParam = None
1104 #
1105 # 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.
1106 postProcSpec = {}
1107 postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
1108 postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
1109 postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
1110 #
1111 # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name
1112 valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
1113 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001114 # Check for NULL pointers, ignore the in-out count parameters that
Mark Lobodzinski85672672016-10-13 08:36:42 -06001115 # will be validated with their associated array
1116 if (value.ispointer or value.isstaticarray) and not value.iscount:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001117 # Parameters for function argument generation
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001118 req = 'true' # Parameter cannot be NULL
Mark Lobodzinski85672672016-10-13 08:36:42 -06001119 cpReq = 'true' # Count pointer cannot be NULL
1120 cvReq = 'true' # Count value cannot be 0
1121 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
Mark Lobodzinski85672672016-10-13 08:36:42 -06001122 # Generate required/optional parameter strings for the pointer and count values
1123 if value.isoptional:
1124 req = 'false'
1125 if value.len:
1126 # The parameter is an array with an explicit count parameter
1127 lenParam = self.getLenParam(values, value.len)
1128 lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
1129 if lenParam.ispointer:
1130 # Count parameters that are pointers are inout
1131 if type(lenParam.isoptional) is list:
1132 if lenParam.isoptional[0]:
1133 cpReq = 'false'
1134 if lenParam.isoptional[1]:
1135 cvReq = 'false'
1136 else:
1137 if lenParam.isoptional:
1138 cpReq = 'false'
1139 else:
1140 if lenParam.isoptional:
1141 cvReq = 'false'
1142 #
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001143 # The parameter will not be processed when tagged as 'noautovalidity'
Mark Lobodzinski85672672016-10-13 08:36:42 -06001144 # For the pointer to struct case, the struct pointer will not be validated, but any
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001145 # members not tagged as 'noautovalidity' will be validated
1146 # We special-case the custom allocator checks, as they are explicit but can be auto-generated.
Ricardo Garcia98ba9642019-02-19 11:25:18 +01001147 AllocatorFunctions = ['PFN_vkAllocationFunction', 'PFN_vkReallocationFunction', 'PFN_vkFreeFunction', 'PFN_vkInternalAllocationNotification', 'PFN_vkInternalFreeNotification']
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001148 if value.noautovalidity and value.type not in AllocatorFunctions:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001149 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1150 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1151 else:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001152 if value.type in self.structTypes:
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001153 # If this is a pointer to a struct with an sType field, verify the type
Mark Lobodzinski9cf24dd2017-06-28 14:23:22 -06001154 usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001155 # 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
1156 elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
1157 usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1158 elif value.type in self.flags and value.isconst:
1159 usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1160 elif value.isbool and value.isconst:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001161 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))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001162 elif value.israngedenum and value.isconst:
Mark Lobodzinskiaff801e2017-07-25 15:29:57 -06001163 enum_value_list = 'All%sEnums' % value.type
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001164 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))
Tony-LunarG53ca8982018-11-07 14:22:13 -07001165 elif value.name == 'pNext' and value.isconst:
Mark Lobodzinski9ddf9282018-05-31 13:59:59 -06001166 usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001167 else:
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -06001168 usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001169 # If this is a pointer to a struct (input), see if it contains members that need to be checked
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001170 if value.type in self.validatedStructs:
1171 if value.isconst: # or value.type in self.returnedonly_structs:
1172 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
1173 elif value.type in self.returnedonly_structs:
1174 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001175 # Non-pointer types
1176 else:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001177 # The parameter will not be processes when tagged as 'noautovalidity'
1178 # For the struct case, the struct type will not be validated, but any
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001179 # members not tagged as 'noautovalidity' will be validated
Mark Lobodzinski85672672016-10-13 08:36:42 -06001180 if value.noautovalidity:
1181 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1182 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1183 else:
Mark Lobodzinski024b2822017-06-27 13:22:05 -06001184 vuid_name_tag = structTypeName if structTypeName is not None else funcName
Mark Lobodzinski85672672016-10-13 08:36:42 -06001185 if value.type in self.structTypes:
1186 stype = self.structTypes[value.type]
Dave Houlton413a6782018-05-22 13:01:54 -06001187 vuid = self.GetVuid(value.type, "sType-sType")
Mark Lobodzinskia16ebc72018-06-15 14:47:39 -06001188 undefined_vuid = '"kVUIDUndefined"'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001189 usedLines.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false, kVUIDUndefined, {});\n'.format(
Mike Schuchardt24ac4e72018-08-11 17:37:20 -07001190 funcName, valueDisplayName, valuePrefix, vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001191 elif value.type in self.handleTypes:
1192 if not self.isHandleOptional(value, None):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001193 usedLines.append('skip |= validate_required_handle("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001194 elif value.type in self.flags:
1195 flagBitsName = value.type.replace('Flags', 'FlagBits')
1196 if not flagBitsName in self.flagBits:
Dave Houlton413a6782018-05-22 13:01:54 -06001197 vuid = self.GetVuid(vuid_name_tag, "%s-zerobitmask" % (value.name))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001198 usedLines.append('skip |= validate_reserved_flags("{}", {ppp}"{}"{pps}, {pf}{}, {});\n'.format(funcName, valueDisplayName, value.name, vuid, pf=valuePrefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001199 else:
Mark Lobodzinskie643fcc2017-06-26 16:27:15 -06001200 if value.isoptional:
1201 flagsRequired = 'false'
Dave Houlton413a6782018-05-22 13:01:54 -06001202 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinskie643fcc2017-06-26 16:27:15 -06001203 else:
1204 flagsRequired = 'true'
Dave Houlton413a6782018-05-22 13:01:54 -06001205 vuid = self.GetVuid(vuid_name_tag, "%s-requiredbitmask" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001206 allFlagsName = 'All' + flagBitsName
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001207 usedLines.append('skip |= validate_flags("{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {}, false, {});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsRequired, vuid, pf=valuePrefix, **postProcSpec))
Mike Schuchardt47619c82017-05-31 09:14:22 -06001208 elif value.type in self.flagBits:
1209 flagsRequired = 'false' if value.isoptional else 'true'
1210 allFlagsName = 'All' + value.type
Dave Houlton413a6782018-05-22 13:01:54 -06001211 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001212 usedLines.append('skip |= validate_flags("{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {}, true, {});\n'.format(funcName, valueDisplayName, value.type, allFlagsName, value.name, flagsRequired, vuid, pf=valuePrefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001213 elif value.isbool:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001214 usedLines.append('skip |= validate_bool32("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001215 elif value.israngedenum:
Dave Houlton413a6782018-05-22 13:01:54 -06001216 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinski74cb45f2017-07-25 15:10:29 -06001217 enum_value_list = 'All%sEnums' % value.type
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001218 usedLines.append('skip |= validate_ranged_enum("{}", {ppp}"{}"{pps}, "{}", {}, {}{}, {});\n'.format(funcName, valueDisplayName, value.type, enum_value_list, valuePrefix, value.name, vuid, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001219 # If this is a struct, see if it contains members that need to be checked
1220 if value.type in self.validatedStructs:
1221 memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
1222 memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001223 usedLines.append(self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001224 # Append the parameter check to the function body for the current command
1225 if usedLines:
1226 # Apply special conditional checks
1227 if value.condition:
1228 usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
1229 lines += usedLines
1230 elif not value.iscount:
1231 # If no expression was generated for this value, it is unreferenced by the validation function, unless
1232 # it is an array count, which is indirectly referenced for array valiadation.
1233 unused.append(value.name)
Mark Lobodzinskid4950072017-08-01 13:02:20 -06001234 if not lines:
1235 lines.append('// No xml-driven validation\n')
Mark Lobodzinski85672672016-10-13 08:36:42 -06001236 return lines, unused
1237 #
1238 # Generate the struct member check code from the captured data
1239 def processStructMemberData(self):
1240 indent = self.incIndent(None)
1241 for struct in self.structMembers:
1242 #
1243 # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
1244 lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
1245 if lines:
1246 self.validatedStructs[struct.name] = lines
1247 #
1248 # Generate the command param check code from the captured data
1249 def processCmdData(self):
1250 indent = self.incIndent(None)
1251 for command in self.commands:
1252 # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
1253 startIndex = 0 if command.name == 'vkCreateInstance' else 1
1254 lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
Mark Lobodzinski3f10bfe2017-08-23 15:23:23 -06001255 # Cannot validate extension dependencies for device extension APIs having a physical device as their dispatchable object
Mike Schuchardtafd00482017-08-24 15:15:02 -06001256 if (command.name in self.required_extensions) and (self.extension_type != 'device' or command.params[0].type != 'VkPhysicalDevice'):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001257 ext_test = ''
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001258 if command.params[0].type in ["VkInstance", "VkPhysicalDevice"] or command.name == 'vkCreateInstance':
1259 ext_table_type = 'instance'
1260 else:
1261 ext_table_type = 'device'
Mike Schuchardtafd00482017-08-24 15:15:02 -06001262 for ext in self.required_extensions[command.name]:
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001263 ext_name_define = ''
1264 ext_enable_name = ''
1265 for extension in self.registry.extensions:
1266 if extension.attrib['name'] == ext:
1267 ext_name_define = extension[0][1].get('name')
1268 ext_enable_name = ext_name_define.lower()
1269 ext_enable_name = re.sub('_extension_name', '', ext_enable_name)
1270 break
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001271 ext_test = 'if (!%s_extensions.%s) skip |= OutputExtensionError("%s", %s);\n' % (ext_table_type, ext_enable_name, command.name, ext_name_define)
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001272 lines.insert(0, ext_test)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001273 if lines:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001274 func_sig = self.getCmdDef(command) + ' {\n'
1275 func_sig = func_sig.split('VKAPI_CALL vk')[1]
1276 cmdDef = 'bool StatelessValidation::PreCallValidate' + func_sig
Mark Lobodzinskid4950072017-08-01 13:02:20 -06001277 cmdDef += '%sbool skip = false;\n' % indent
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001278 # Insert call to custom-written function if present
1279 if command.name in self.functions_with_manual_checks:
1280 # Generate parameter list for manual fcn and down-chain calls
1281 params_text = ''
1282 for param in command.params:
1283 params_text += '%s, ' % param.name
1284 params_text = params_text[:-2] + ');\n'
1285 cmdDef += ' skip |= manual_PreCallValidate'+ command.name[2:] + '(' + params_text
Mark Lobodzinski85672672016-10-13 08:36:42 -06001286 for line in lines:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001287 if type(line) is list:
1288 for sub in line:
1289 cmdDef += indent + sub
1290 else:
1291 cmdDef += indent + line
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001292 cmdDef += '%sreturn skip;\n' % indent
Mark Lobodzinski85672672016-10-13 08:36:42 -06001293 cmdDef += '}\n'
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001294 self.validation.append(cmdDef)