blob: 8aa410f1875a022cb0c7e0552e416f3d5e4f9222 [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',
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700143 'vkCreateGraphicsPipelines',
144 'vkCreateComputePipelines',
Peter Chen85366392019-05-14 15:20:11 -0400145 "vkCreateRayTracingPipelinesNV",
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700146 'vkCreateSampler',
147 'vkCreateDescriptorSetLayout',
148 'vkFreeDescriptorSets',
149 'vkUpdateDescriptorSets',
150 'vkCreateRenderPass',
151 'vkCreateRenderPass2KHR',
152 'vkBeginCommandBuffer',
153 'vkCmdSetViewport',
154 'vkCmdSetScissor',
155 'vkCmdSetLineWidth',
156 'vkCmdDraw',
157 'vkCmdDrawIndirect',
158 'vkCmdDrawIndexedIndirect',
Mark Lobodzinskif77a4ac2019-06-27 15:30:51 -0600159 'vkCmdClearAttachments',
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700160 'vkCmdCopyImage',
Piers Daniell8fd03f52019-08-21 12:07:53 -0600161 'vkCmdBindIndexBuffer',
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700162 'vkCmdBlitImage',
163 'vkCmdCopyBufferToImage',
164 'vkCmdCopyImageToBuffer',
165 'vkCmdUpdateBuffer',
166 'vkCmdFillBuffer',
167 'vkCreateSwapchainKHR',
168 'vkQueuePresentKHR',
169 'vkCreateDescriptorPool',
170 'vkCmdDispatch',
171 'vkCmdDispatchIndirect',
172 'vkCmdDispatchBaseKHR',
173 'vkCmdSetExclusiveScissorNV',
174 'vkCmdSetViewportShadingRatePaletteNV',
175 'vkCmdSetCoarseSampleOrderNV',
176 'vkCmdDrawMeshTasksNV',
177 'vkCmdDrawMeshTasksIndirectNV',
178 'vkCmdDrawMeshTasksIndirectCountNV',
Jeff Bolz7e7e6e02019-01-11 22:53:41 -0600179 'vkAllocateMemory',
Ricardo Garciaa4935972019-02-21 17:43:18 +0100180 'vkCreateAccelerationStructureNV',
Jason Macnak5c954952019-07-09 15:46:12 -0700181 'vkGetAccelerationStructureHandleNV',
182 'vkCmdBuildAccelerationStructureNV',
Tobias Hectorebb855f2019-07-23 12:17:33 +0100183 'vkCreateFramebuffer',
Jeff Bolz8125a8b2019-08-16 16:29:45 -0500184 'vkCmdSetLineStippleEXT',
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700185 ]
186
Mark Lobodzinski85672672016-10-13 08:36:42 -0600187 # Commands to ignore
188 self.blacklist = [
189 'vkGetInstanceProcAddr',
190 'vkGetDeviceProcAddr',
Mark Young6ba8abe2017-11-09 10:37:04 -0700191 'vkEnumerateInstanceVersion',
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600192 'vkEnumerateInstanceLayerProperties',
193 'vkEnumerateInstanceExtensionProperties',
194 'vkEnumerateDeviceLayerProperties',
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600195 'vkEnumerateDeviceExtensionProperties',
Mike Schuchardt21638df2019-03-16 10:52:02 -0700196 'vkGetDeviceGroupSurfacePresentModes2EXT'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600197 ]
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700198
Dustin Gravesce68f082017-03-30 15:42:16 -0600199 # Structure fields to ignore
200 self.structMemberBlacklist = { 'VkWriteDescriptorSet' : ['dstSet'] }
Mark Lobodzinski85672672016-10-13 08:36:42 -0600201 # Validation conditions for some special case struct members that are conditionally validated
202 self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
203 # Header version
204 self.headerVersion = None
205 # Internal state - accumulators for different inner block text
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600206 self.validation = [] # Text comprising the main per-api parameter validation routines
Mark Lobodzinski85672672016-10-13 08:36:42 -0600207 self.stypes = [] # Values from the VkStructureType enumeration
208 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
209 self.handleTypes = set() # Set of handle type names
210 self.commands = [] # List of CommandData records for all Vulkan commands
211 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
212 self.validatedStructs = dict() # Map of structs type names to generated validation code for that struct type
213 self.enumRanges = dict() # Map of enum name to BEGIN/END range values
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600214 self.enumValueLists = '' # String containing enumerated type map definitions
Mark Lobodzinski85672672016-10-13 08:36:42 -0600215 self.flags = set() # Map of flags typenames
216 self.flagBits = dict() # Map of flag bits typename to list of values
Chris Forbes78ea32d2016-11-28 11:14:17 +1300217 self.newFlags = set() # Map of flags typenames /defined in the current feature/
Mike Schuchardtafd00482017-08-24 15:15:02 -0600218 self.required_extensions = dict() # Dictionary of required extensions for each item in the current extension
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600219 self.extension_type = '' # Type of active feature (extension), device or instance
220 self.extension_names = dict() # Dictionary of extension names to extension name defines
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600221 self.structextends_list = [] # List of extensions which extend another struct
222 self.struct_feature_protect = dict() # Dictionary of structnames and FeatureExtraProtect strings
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600223 self.valid_vuids = set() # Set of all valid VUIDs
Dave Houlton413a6782018-05-22 13:01:54 -0600224 self.vuid_dict = dict() # VUID dictionary (from JSON)
225 self.alias_dict = dict() # Dict of cmd|struct aliases
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700226 self.header_file = False # Header file generation flag
227 self.source_file = False # Source file generation flag
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600228 self.returnedonly_structs = []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600229 # Named tuples to store struct and command data
Mark Lobodzinski85672672016-10-13 08:36:42 -0600230 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600231 'isconst', 'isoptional', 'iscount', 'noautovalidity',
232 'len', 'extstructs', 'condition', 'cdecl'])
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600233 self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl', 'extension_type', 'result'])
Mark Lobodzinski85672672016-10-13 08:36:42 -0600234 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600235
Mark Lobodzinski85672672016-10-13 08:36:42 -0600236 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600237 # Generate Copyright comment block for file
238 def GenerateCopyright(self):
239 copyright = '/* *** THIS FILE IS GENERATED - DO NOT EDIT! ***\n'
240 copyright += ' * See parameter_validation_generator.py for modifications\n'
241 copyright += ' *\n'
Mike Schuchardt840b5042019-07-11 08:11:47 -0700242 copyright += ' * Copyright (c) 2015-2019 The Khronos Group Inc.\n'
243 copyright += ' * Copyright (c) 2015-2019 LunarG, Inc.\n'
244 copyright += ' * Copyright (C) 2015-2019 Google Inc.\n'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600245 copyright += ' *\n'
246 copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
247 copyright += ' * you may not use this file except in compliance with the License.\n'
248 copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n'
249 copyright += ' * You may obtain a copy of the License at\n'
250 copyright += ' *\n'
251 copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n'
252 copyright += ' *\n'
253 copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
254 copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
255 copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
256 copyright += ' * See the License for the specific language governing permissions and\n'
257 copyright += ' * limitations under the License.\n'
258 copyright += ' *\n'
259 copyright += ' * Author: Mark Lobodzinski <mark@LunarG.com>\n'
Dave Houlton413a6782018-05-22 13:01:54 -0600260 copyright += ' * Author: Dave Houlton <daveh@LunarG.com>\n'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600261 copyright += ' */\n\n'
262 return copyright
263 #
264 # Increases the global indent variable
Mark Lobodzinski85672672016-10-13 08:36:42 -0600265 def incIndent(self, indent):
266 inc = ' ' * self.INDENT_SPACES
267 if indent:
268 return indent + inc
269 return inc
270 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600271 # Decreases the global indent variable
Mark Lobodzinski85672672016-10-13 08:36:42 -0600272 def decIndent(self, indent):
273 if indent and (len(indent) > self.INDENT_SPACES):
274 return indent[:-self.INDENT_SPACES]
275 return ''
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600276 #
Dave Houlton413a6782018-05-22 13:01:54 -0600277 # Walk the JSON-derived dict and find all "vuid" key values
278 def ExtractVUIDs(self, d):
279 if hasattr(d, 'items'):
280 for k, v in d.items():
281 if k == "vuid":
282 yield v
283 elif isinstance(v, dict):
284 for s in self.ExtractVUIDs(v):
285 yield s
286 elif isinstance (v, list):
287 for l in v:
288 for s in self.ExtractVUIDs(l):
289 yield s
Mark Lobodzinski85672672016-10-13 08:36:42 -0600290 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600291 # Called at file creation time
Mark Lobodzinski85672672016-10-13 08:36:42 -0600292 def beginFile(self, genOpts):
293 OutputGenerator.beginFile(self, genOpts)
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700294 self.header_file = (genOpts.filename == 'parameter_validation.h')
295 self.source_file = (genOpts.filename == 'parameter_validation.cpp')
296
297 if not self.header_file and not self.source_file:
298 print("Error: Output Filenames have changed, update generator source.\n")
299 sys.exit(1)
300
301 if self.source_file or self.header_file:
302 # Output Copyright text
303 s = self.GenerateCopyright()
304 write(s, file=self.outFile)
305
306 if self.header_file:
307 return
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -0600308
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700309 # Build map of structure type names to VkStructureType enum values
310 # Find all types of category "struct"
311 for struct in self.registry.tree.iterfind('types/type[@category="struct"]'):
312 # Check if struct has member named "sType" of type "VkStructureType" which has values defined
313 stype = struct.find('member[name="sType"][type="VkStructureType"][@values]')
Shahbaz Youssefi23aee922019-01-11 14:04:49 -0500314 if stype is not None:
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700315 # Store VkStructureType value for this type
316 self.structTypes[struct.get('name')] = stype.get('values')
317
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -0600318 self.valid_usage_path = genOpts.valid_usage_path
319 vu_json_filename = os.path.join(self.valid_usage_path + os.sep, 'validusage.json')
320 if os.path.isfile(vu_json_filename):
321 json_file = open(vu_json_filename, 'r')
322 self.vuid_dict = json.load(json_file)
323 json_file.close()
324 if len(self.vuid_dict) == 0:
325 print("Error: Could not find, or error loading %s/validusage.json\n", vu_json_filename)
326 sys.exit(1)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600327 #
Dave Houlton413a6782018-05-22 13:01:54 -0600328 # Build a set of all vuid text strings found in validusage.json
329 for json_vuid_string in self.ExtractVUIDs(self.vuid_dict):
330 self.valid_vuids.add(json_vuid_string)
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600331 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600332 # Headers
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700333 write('#include "chassis.h"', file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600334 self.newline()
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700335 write('#include "stateless_validation.h"', file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600336 self.newline()
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600337 #
338 # Called at end-time for final content output
Mark Lobodzinski85672672016-10-13 08:36:42 -0600339 def endFile(self):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700340 if self.source_file:
341 # C-specific
342 self.newline()
343 write(self.enumValueLists, file=self.outFile)
344 self.newline()
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600345
Locke6b6b7382019-04-16 15:08:49 -0600346 pnext_handler = 'bool StatelessValidation::ValidatePnextStructContents(const char *api_name, const ParameterName &parameter_name, const VkBaseOutStructure* header) {\n'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700347 pnext_handler += ' bool skip = false;\n'
348 pnext_handler += ' switch(header->sType) {\n'
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600349
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700350 # Do some processing here to extract data from validatedstructs...
351 for item in self.structextends_list:
352 postProcSpec = {}
353 postProcSpec['ppp'] = '' if not item else '{postProcPrefix}'
354 postProcSpec['pps'] = '' if not item else '{postProcSuffix}'
355 postProcSpec['ppi'] = '' if not item else '{postProcInsert}'
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600356
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700357 pnext_case = '\n'
358 protect = ''
359 # Guard struct cases with feature ifdefs, if necessary
360 if item in self.struct_feature_protect.keys():
361 protect = self.struct_feature_protect[item]
362 pnext_case += '#ifdef %s\n' % protect
363 pnext_case += ' // Validation code for %s structure members\n' % item
364 pnext_case += ' case %s: {\n' % self.structTypes[item]
365 pnext_case += ' %s *structure = (%s *) header;\n' % (item, item)
366 expr = self.expandStructCode(item, item, 'structure->', '', ' ', [], postProcSpec)
367 struct_validation_source = self.ScrubStructCode(expr)
368 pnext_case += '%s' % struct_validation_source
369 pnext_case += ' } break;\n'
Raul Tambre7b300182019-05-04 11:25:14 +0300370 if protect:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700371 pnext_case += '#endif // %s\n' % protect
372 # Skip functions containing no validation
Raul Tambre7b300182019-05-04 11:25:14 +0300373 if struct_validation_source:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700374 pnext_handler += pnext_case;
375 pnext_handler += ' default:\n'
376 pnext_handler += ' skip = false;\n'
377 pnext_handler += ' }\n'
378 pnext_handler += ' return skip;\n'
379 pnext_handler += '}\n'
380 write(pnext_handler, file=self.outFile)
381 self.newline()
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600382
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700383 ext_template = 'bool StatelessValidation::OutputExtensionError(const std::string &api_name, const std::string &extension_name) {\n'
384 ext_template += ' return log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,\n'
385 ext_template += ' kVUID_PVError_ExtensionNotEnabled, "Attemped to call %s() but its required extension %s has not been enabled\\n",\n'
386 ext_template += ' api_name.c_str(), extension_name.c_str());\n'
387 ext_template += '}\n'
388 write(ext_template, file=self.outFile)
389 self.newline()
390 commands_text = '\n'.join(self.validation)
391 write(commands_text, file=self.outFile)
392 self.newline()
393 if self.header_file:
394 # Output declarations and record intercepted procedures
395 write('\n'.join(self.declarations), file=self.outFile)
396 # Finish processing in superclass
397 OutputGenerator.endFile(self)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600398 #
399 # Processing at beginning of each feature or extension
Mark Lobodzinski85672672016-10-13 08:36:42 -0600400 def beginFeature(self, interface, emit):
401 # Start processing in superclass
402 OutputGenerator.beginFeature(self, interface, emit)
403 # C-specific
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600404 # Accumulate includes, defines, types, enums, function pointer typedefs, end function prototypes separately for this
405 # feature. They're only printed in endFeature().
Mark Lobodzinski85672672016-10-13 08:36:42 -0600406 self.headerVersion = None
Mark Lobodzinski85672672016-10-13 08:36:42 -0600407 self.stypes = []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600408 self.commands = []
409 self.structMembers = []
Chris Forbes78ea32d2016-11-28 11:14:17 +1300410 self.newFlags = set()
Mark Lobodzinski62f71562017-10-24 13:41:18 -0600411 self.featureExtraProtect = GetFeatureProtect(interface)
Mike Schuchardtafd00482017-08-24 15:15:02 -0600412 # Get base list of extension dependencies for all items in this extension
413 base_required_extensions = []
Mark Lobodzinski31964ca2017-09-18 14:15:09 -0600414 if "VK_VERSION_1" not in self.featureName:
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600415 # Save Name Define to get correct enable name later
416 nameElem = interface[0][1]
417 name = nameElem.get('name')
418 self.extension_names[self.featureName] = name
419 # This extension is the first dependency for this command
Mike Schuchardtafd00482017-08-24 15:15:02 -0600420 base_required_extensions.append(self.featureName)
421 # Add any defined extension dependencies to the base dependency list for this extension
422 requires = interface.get('requires')
423 if requires is not None:
424 base_required_extensions.extend(requires.split(','))
Mike Schuchardtafd00482017-08-24 15:15:02 -0600425 # Build dictionary of extension dependencies for each item in this extension
426 self.required_extensions = dict()
427 for require_element in interface.findall('require'):
428 # Copy base extension dependency list
429 required_extensions = list(base_required_extensions)
430 # Add any additional extension dependencies specified in this require block
431 additional_extensions = require_element.get('extension')
432 if additional_extensions:
433 required_extensions.extend(additional_extensions.split(','))
434 # Save full extension list for all named items
435 for element in require_element.findall('*[@name]'):
436 self.required_extensions[element.get('name')] = required_extensions
437
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600438 # And note if this is an Instance or Device extension
439 self.extension_type = interface.get('type')
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600440 #
441 # Called at the end of each extension (feature)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600442 def endFeature(self):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700443 if self.header_file:
444 return
Mark Lobodzinski85672672016-10-13 08:36:42 -0600445 # C-specific
446 # Actually write the interface to the output file.
447 if (self.emit):
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600448 # 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 -0600449 # or move it below the 'for section...' loop.
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600450 ifdef = ''
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100451 if (self.featureExtraProtect is not None):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600452 ifdef = '#ifdef %s\n' % self.featureExtraProtect
453 self.validation.append(ifdef)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600454 # Generate the struct member checking code from the captured data
455 self.processStructMemberData()
456 # Generate the command parameter checking code from the captured data
457 self.processCmdData()
458 # Write the declaration for the HeaderVersion
459 if self.headerVersion:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700460 write('const uint32_t GeneratedVulkanHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600461 self.newline()
462 # Write the declarations for the VkFlags values combining all flag bits
Chris Forbes78ea32d2016-11-28 11:14:17 +1300463 for flag in sorted(self.newFlags):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600464 flagBits = flag.replace('Flags', 'FlagBits')
465 if flagBits in self.flagBits:
466 bits = self.flagBits[flagBits]
467 decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
468 for bit in bits[1:]:
469 decl += '|' + bit
470 decl += ';'
471 write(decl, file=self.outFile)
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600472 endif = '\n'
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100473 if (self.featureExtraProtect is not None):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600474 endif = '#endif // %s\n' % self.featureExtraProtect
475 self.validation.append(endif)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600476 # Finish processing in superclass
477 OutputGenerator.endFeature(self)
478 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600479 # Type generation
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700480 def genType(self, typeinfo, name, alias):
Dave Houlton413a6782018-05-22 13:01:54 -0600481 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100482 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600483 self.alias_dict[name]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700484 OutputGenerator.genType(self, typeinfo, name, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600485 typeElem = typeinfo.elem
Mark Lobodzinski87017df2018-05-30 11:29:24 -0600486 # 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 -0600487 category = typeElem.get('category')
488 if (category == 'struct' or category == 'union'):
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700489 self.genStruct(typeinfo, name, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600490 elif (category == 'handle'):
491 self.handleTypes.add(name)
492 elif (category == 'bitmask'):
493 self.flags.add(name)
Chris Forbes78ea32d2016-11-28 11:14:17 +1300494 self.newFlags.add(name)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600495 elif (category == 'define'):
496 if name == 'VK_HEADER_VERSION':
497 nameElem = typeElem.find('name')
498 self.headerVersion = noneStr(nameElem.tail).strip()
499 #
500 # Struct parameter check generation.
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600501 # This is a special case of the <type> tag where the contents are interpreted as a set of <member> tags instead of freeform C
502 # type declarations. The <member> tags are just like <param> tags - they are a declaration of a struct or union member.
503 # Only simple member declarations are supported (no nested structs etc.)
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700504 def genStruct(self, typeinfo, typeName, alias):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700505 if not self.source_file:
506 return
Dave Houlton413a6782018-05-22 13:01:54 -0600507 # alias has already been recorded in genType, above
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700508 OutputGenerator.genStruct(self, typeinfo, typeName, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600509 conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
510 members = typeinfo.elem.findall('.//member')
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600511 if self.featureExtraProtect is not None:
512 self.struct_feature_protect[typeName] = self.featureExtraProtect
Mark Lobodzinski85672672016-10-13 08:36:42 -0600513 #
514 # Iterate over members once to get length parameters for arrays
515 lens = set()
516 for member in members:
517 len = self.getLen(member)
518 if len:
519 lens.add(len)
520 #
521 # Generate member info
522 membersInfo = []
523 for member in members:
524 # Get the member's type and name
525 info = self.getTypeNameTuple(member)
526 type = info[0]
527 name = info[1]
528 stypeValue = ''
529 cdecl = self.makeCParamDecl(member, 0)
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700530
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600531 # Store pointer/array/string info -- Check for parameter name in lens set
Mark Lobodzinski85672672016-10-13 08:36:42 -0600532 iscount = False
533 if name in lens:
534 iscount = True
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600535 # The pNext members are not tagged as optional, but are treated as optional for parameter NULL checks. Static array
536 # members are also treated as optional to skip NULL pointer validation, as they won't be NULL.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600537 isstaticarray = self.paramIsStaticArray(member)
538 isoptional = False
539 if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
540 isoptional = True
Dustin Gravesce68f082017-03-30 15:42:16 -0600541 # Determine if value should be ignored by code generation.
542 noautovalidity = False
543 if (member.attrib.get('noautovalidity') is not None) or ((typeName in self.structMemberBlacklist) and (name in self.structMemberBlacklist[typeName])):
544 noautovalidity = True
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600545 structextends = False
Mark Lobodzinski85672672016-10-13 08:36:42 -0600546 membersInfo.append(self.CommandParam(type=type, name=name,
547 ispointer=self.paramIsPointer(member),
548 isstaticarray=isstaticarray,
549 isbool=True if type == 'VkBool32' else False,
550 israngedenum=True if type in self.enumRanges else False,
551 isconst=True if 'const' in cdecl else False,
552 isoptional=isoptional,
553 iscount=iscount,
Dustin Gravesce68f082017-03-30 15:42:16 -0600554 noautovalidity=noautovalidity,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600555 len=self.getLen(member),
Mike Schuchardta40d0b02017-07-23 12:47:47 -0600556 extstructs=self.registry.validextensionstructs[typeName] if name == 'pNext' else None,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600557 condition=conditions[name] if conditions and name in conditions else None,
558 cdecl=cdecl))
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600559 # If this struct extends another, keep its name in list for further processing
560 if typeinfo.elem.attrib.get('structextends') is not None:
561 self.structextends_list.append(typeName)
562 # Returnedonly structs should have most of their members ignored -- on entry, we only care about validating the sType and
563 # pNext members. Everything else will be overwritten by the callee.
564 if typeinfo.elem.attrib.get('returnedonly') is not None:
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600565 self.returnedonly_structs.append(typeName)
566 membersInfo = [m for m in membersInfo if m.name in ('sType', 'pNext')]
Mark Lobodzinski85672672016-10-13 08:36:42 -0600567 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
568 #
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600569 # Capture group (e.g. C "enum" type) info to be used for param check code generation.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600570 # These are concatenated together with other types.
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700571 def genGroup(self, groupinfo, groupName, alias):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700572 if not self.source_file:
573 return
Dave Houlton413a6782018-05-22 13:01:54 -0600574 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100575 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600576 self.alias_dict[groupName]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700577 OutputGenerator.genGroup(self, groupinfo, groupName, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600578 groupElem = groupinfo.elem
Mark Lobodzinski85672672016-10-13 08:36:42 -0600579 # Store the sType values
580 if groupName == 'VkStructureType':
581 for elem in groupElem.findall('enum'):
582 self.stypes.append(elem.get('name'))
583 elif 'FlagBits' in groupName:
584 bits = []
585 for elem in groupElem.findall('enum'):
Shannon McPherson533a66c2018-08-21 12:09:25 -0600586 if elem.get('supported') != 'disabled':
587 bits.append(elem.get('name'))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600588 if bits:
589 self.flagBits[groupName] = bits
590 else:
591 # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
592 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
593 expandPrefix = expandName
594 expandSuffix = ''
595 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
596 if expandSuffixMatch:
597 expandSuffix = '_' + expandSuffixMatch.group()
598 # Strip off the suffix from the prefix
599 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
600 isEnum = ('FLAG_BITS' not in expandPrefix)
601 if isEnum:
602 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600603 # Create definition for a list containing valid enum values for this enumerated type
Mike Schuchardt21638df2019-03-16 10:52:02 -0700604 if self.featureExtraProtect is not None:
605 enum_entry = '\n#ifdef %s\n' % self.featureExtraProtect
606 else:
607 enum_entry = ''
608 enum_entry += 'const std::vector<%s> All%sEnums = {' % (groupName, groupName)
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600609 for enum in groupElem:
610 name = enum.get('name')
Mark Lobodzinski117d88f2017-07-27 12:09:08 -0600611 if name is not None and enum.get('supported') != 'disabled':
612 enum_entry += '%s, ' % name
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600613 enum_entry += '};\n'
Mike Schuchardt21638df2019-03-16 10:52:02 -0700614 if self.featureExtraProtect is not None:
Jason Macnak49f32c32019-07-29 14:38:44 -0700615 enum_entry += '#endif // %s\n' % self.featureExtraProtect
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600616 self.enumValueLists += enum_entry
Mark Lobodzinski85672672016-10-13 08:36:42 -0600617 #
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600618 # Capture command parameter info to be used for param check code generation.
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700619 def genCmd(self, cmdinfo, name, alias):
Dave Houlton413a6782018-05-22 13:01:54 -0600620 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100621 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600622 self.alias_dict[name]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700623 OutputGenerator.genCmd(self, cmdinfo, name, alias)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600624 decls = self.makeCDecls(cmdinfo.elem)
625 typedef = decls[1]
626 typedef = typedef.split(')',1)[1]
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700627 if self.header_file:
628 if name not in self.blacklist:
629 if (self.featureExtraProtect is not None):
630 self.declarations += [ '#ifdef %s' % self.featureExtraProtect ]
631 # Strip off 'vk' from API name
632 self.declarations += [ '%s%s' % ('bool PreCallValidate', decls[0].split("VKAPI_CALL vk")[1])]
633 if (self.featureExtraProtect is not None):
634 self.declarations += [ '#endif' ]
635 if self.source_file:
636 if name not in self.blacklist:
637 params = cmdinfo.elem.findall('param')
638 # Get list of array lengths
639 lens = set()
640 for param in params:
641 len = self.getLen(param)
642 if len:
643 lens.add(len)
644 # Get param info
645 paramsInfo = []
646 for param in params:
647 paramInfo = self.getTypeNameTuple(param)
648 cdecl = self.makeCParamDecl(param, 0)
649 # Check for parameter name in lens set
650 iscount = False
651 if paramInfo[1] in lens:
652 iscount = True
653 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
654 ispointer=self.paramIsPointer(param),
655 isstaticarray=self.paramIsStaticArray(param),
656 isbool=True if paramInfo[0] == 'VkBool32' else False,
657 israngedenum=True if paramInfo[0] in self.enumRanges else False,
658 isconst=True if 'const' in cdecl else False,
659 isoptional=self.paramIsOptional(param),
660 iscount=iscount,
661 noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
662 len=self.getLen(param),
663 extstructs=None,
664 condition=None,
665 cdecl=cdecl))
666 # Save return value information, if any
667 result_type = ''
668 resultinfo = cmdinfo.elem.find('proto/type')
669 if (resultinfo is not None and resultinfo.text != 'void'):
670 result_type = resultinfo.text
671 self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0], extension_type=self.extension_type, result=result_type))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600672 #
673 # Check if the parameter passed in is a pointer
674 def paramIsPointer(self, param):
675 ispointer = 0
676 paramtype = param.find('type')
677 if (paramtype.tail is not None) and ('*' in paramtype.tail):
678 ispointer = paramtype.tail.count('*')
679 elif paramtype.text[:4] == 'PFN_':
680 # Treat function pointer typedefs as a pointer to a single value
681 ispointer = 1
682 return ispointer
683 #
684 # Check if the parameter passed in is a static array
685 def paramIsStaticArray(self, param):
686 isstaticarray = 0
687 paramname = param.find('name')
688 if (paramname.tail is not None) and ('[' in paramname.tail):
689 isstaticarray = paramname.tail.count('[')
690 return isstaticarray
691 #
692 # Check if the parameter passed in is optional
693 # Returns a list of Boolean values for comma separated len attributes (len='false,true')
694 def paramIsOptional(self, param):
695 # See if the handle is optional
696 isoptional = False
697 # Simple, if it's optional, return true
698 optString = param.attrib.get('optional')
699 if optString:
700 if optString == 'true':
701 isoptional = True
702 elif ',' in optString:
703 opts = []
704 for opt in optString.split(','):
705 val = opt.strip()
706 if val == 'true':
707 opts.append(True)
708 elif val == 'false':
709 opts.append(False)
710 else:
711 print('Unrecognized len attribute value',val)
712 isoptional = opts
713 return isoptional
714 #
715 # Check if the handle passed in is optional
716 # Uses the same logic as ValidityOutputGenerator.isHandleOptional
717 def isHandleOptional(self, param, lenParam):
718 # Simple, if it's optional, return true
719 if param.isoptional:
720 return True
721 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
722 if param.noautovalidity:
723 return True
724 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
725 if lenParam and lenParam.isoptional:
726 return True
727 return False
728 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600729 # Retrieve the value of the len tag
730 def getLen(self, param):
731 result = None
732 len = param.attrib.get('len')
733 if len and len != 'null-terminated':
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600734 # For string arrays, 'len' can look like 'count,null-terminated', indicating that we have a null terminated array of
735 # strings. We strip the null-terminated from the 'len' field and only return the parameter specifying the string count
Mark Lobodzinski85672672016-10-13 08:36:42 -0600736 if 'null-terminated' in len:
737 result = len.split(',')[0]
738 else:
739 result = len
740 result = str(result).replace('::', '->')
741 return result
742 #
743 # Retrieve the type and name for a parameter
744 def getTypeNameTuple(self, param):
745 type = ''
746 name = ''
747 for elem in param:
748 if elem.tag == 'type':
749 type = noneStr(elem.text)
750 elif elem.tag == 'name':
751 name = noneStr(elem.text)
752 return (type, name)
753 #
754 # Find a named parameter in a parameter list
755 def getParamByName(self, params, name):
756 for param in params:
757 if param.name == name:
758 return param
759 return None
760 #
761 # Extract length values from latexmath. Currently an inflexible solution that looks for specific
762 # patterns that are found in vk.xml. Will need to be updated when new patterns are introduced.
763 def parseLateXMath(self, source):
764 name = 'ERROR'
765 decoratedName = 'ERROR'
766 if 'mathit' in source:
Mark Lobodzinski36c33862017-02-13 10:15:53 -0700767 # Matches expressions similar to 'latexmath:[\lceil{\mathit{rasterizationSamples} \over 32}\rceil]'
768 match = re.match(r'latexmath\s*\:\s*\[\s*\\l(\w+)\s*\{\s*\\mathit\s*\{\s*(\w+)\s*\}\s*\\over\s*(\d+)\s*\}\s*\\r(\w+)\s*\]', source)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600769 if not match or match.group(1) != match.group(4):
770 raise 'Unrecognized latexmath expression'
771 name = match.group(2)
772 decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
773 else:
Mark Lobodzinski36c33862017-02-13 10:15:53 -0700774 # Matches expressions similar to 'latexmath : [dataSize \over 4]'
Shannon McPhersonbd68df02018-10-29 15:04:41 -0600775 match = re.match(r'latexmath\s*\:\s*\[\s*(\\textrm\{)?(\w+)\}?\s*\\over\s*(\d+)\s*\]', source)
776 name = match.group(2)
777 decoratedName = '{}/{}'.format(*match.group(2, 3))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600778 return name, decoratedName
779 #
780 # Get the length paramater record for the specified parameter name
781 def getLenParam(self, params, name):
782 lenParam = None
783 if name:
784 if '->' in name:
785 # The count is obtained by dereferencing a member of a struct parameter
786 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600787 isstaticarray=None, isoptional=False, type=None, noautovalidity=False,
788 len=None, extstructs=None, condition=None, cdecl=None)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600789 elif 'latexmath' in name:
790 lenName, decoratedName = self.parseLateXMath(name)
791 lenParam = self.getParamByName(params, lenName)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600792 else:
793 lenParam = self.getParamByName(params, name)
794 return lenParam
795 #
796 # Convert a vulkan.h command declaration into a parameter_validation.h definition
797 def getCmdDef(self, cmd):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600798 # Strip the trailing ';' and split into individual lines
799 lines = cmd.cdecl[:-1].split('\n')
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600800 cmd_hdr = '\n'.join(lines)
801 return cmd_hdr
Mark Lobodzinski85672672016-10-13 08:36:42 -0600802 #
803 # Generate the code to check for a NULL dereference before calling the
804 # validation function
805 def genCheckedLengthCall(self, name, exprs):
806 count = name.count('->')
807 if count:
808 checkedExpr = []
809 localIndent = ''
810 elements = name.split('->')
811 # Open the if expression blocks
812 for i in range(0, count):
813 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
814 localIndent = self.incIndent(localIndent)
815 # Add the validation expression
816 for expr in exprs:
817 checkedExpr.append(localIndent + expr)
818 # Close the if blocks
819 for i in range(0, count):
820 localIndent = self.decIndent(localIndent)
821 checkedExpr.append(localIndent + '}\n')
822 return [checkedExpr]
823 # No if statements were required
824 return exprs
825 #
826 # Generate code to check for a specific condition before executing validation code
827 def genConditionalCall(self, prefix, condition, exprs):
828 checkedExpr = []
829 localIndent = ''
830 formattedCondition = condition.format(prefix)
831 checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
832 checkedExpr.append(localIndent + '{\n')
833 localIndent = self.incIndent(localIndent)
834 for expr in exprs:
835 checkedExpr.append(localIndent + expr)
836 localIndent = self.decIndent(localIndent)
837 checkedExpr.append(localIndent + '}\n')
838 return [checkedExpr]
839 #
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600840 # Get VUID identifier from implicit VUID tag
Dave Houlton413a6782018-05-22 13:01:54 -0600841 def GetVuid(self, name, suffix):
842 vuid_string = 'VUID-%s-%s' % (name, suffix)
843 vuid = "kVUIDUndefined"
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600844 if '->' in vuid_string:
Dave Houlton413a6782018-05-22 13:01:54 -0600845 return vuid
846 if vuid_string in self.valid_vuids:
847 vuid = "\"%s\"" % vuid_string
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600848 else:
Dave Houlton413a6782018-05-22 13:01:54 -0600849 if name in self.alias_dict:
850 alias_string = 'VUID-%s-%s' % (self.alias_dict[name], suffix)
851 if alias_string in self.valid_vuids:
Shannon McPherson23d97212019-02-18 13:39:42 -0700852 vuid = "\"%s\"" % alias_string
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600853 return vuid
854 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600855 # Generate the sType check string
Mark Lobodzinski9cf24dd2017-06-28 14:23:22 -0600856 def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600857 checkExpr = []
858 stype = self.structTypes[value.type]
Mark Lobodzinski59603552018-05-29 16:14:59 -0600859 vuid_name = struct_type_name if struct_type_name is not None else funcPrintName
860 stype_vuid = self.GetVuid(value.type, "sType-sType")
861 param_vuid = self.GetVuid(vuid_name, "%s-parameter" % value.name)
862
Mark Lobodzinski85672672016-10-13 08:36:42 -0600863 if lenValue:
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800864 count_required_vuid = self.GetVuid(vuid_name, "%s-arraylength" % lenValue.name)
865
Mark Lobodzinski85672672016-10-13 08:36:42 -0600866 # This is an array with a pointer to a count value
867 if lenValue.ispointer:
868 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800869 checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {}, {});\n'.format(
870 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, stype_vuid, param_vuid, count_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype, pf=prefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600871 # This is an array with an integer count value
872 else:
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800873 checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {});\n'.format(
874 funcPrintName, lenValueRequired, valueRequired, stype_vuid, param_vuid, count_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype, pf=prefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600875 # This is an individual struct
876 else:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700877 checkExpr.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {}, {}, {});\n'.format(
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700878 funcPrintName, valuePrintName, prefix, valueRequired, param_vuid, stype_vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600879 return checkExpr
880 #
881 # Generate the handle check string
882 def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
883 checkExpr = []
884 if lenValue:
885 if lenValue.ispointer:
886 # This is assumed to be an output array with a pointer to a count value
887 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
888 else:
889 # This is an array with an integer count value
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700890 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 -0600891 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
892 else:
893 # This is assumed to be an output handle pointer
894 raise('Unsupported parameter validation case: Output handles are not NULL checked')
895 return checkExpr
896 #
897 # Generate check string for an array of VkFlags values
898 def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
899 checkExpr = []
900 flagBitsName = value.type.replace('Flags', 'FlagBits')
901 if not flagBitsName in self.flagBits:
902 raise('Unsupported parameter validation case: array of reserved VkFlags')
903 else:
904 allFlags = 'All' + flagBitsName
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700905 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 -0600906 return checkExpr
907 #
908 # Generate pNext check string
Mark Lobodzinski3c828522017-06-26 13:05:57 -0600909 def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600910 checkExpr = []
911 # Generate an array of acceptable VkStructureType values for pNext
912 extStructCount = 0
913 extStructVar = 'NULL'
914 extStructNames = 'NULL'
Dave Houlton413a6782018-05-22 13:01:54 -0600915 vuid = self.GetVuid(struct_type_name, "pNext-pNext")
Mark Lobodzinski85672672016-10-13 08:36:42 -0600916 if value.extstructs:
Mike Schuchardtc73d07e2017-07-12 10:10:01 -0600917 extStructVar = 'allowed_structs_{}'.format(struct_type_name)
918 extStructCount = 'ARRAY_SIZE({})'.format(extStructVar)
Mike Schuchardta40d0b02017-07-23 12:47:47 -0600919 extStructNames = '"' + ', '.join(value.extstructs) + '"'
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700920 checkExpr.append('const VkStructureType {}[] = {{ {} }};\n'.format(extStructVar, ', '.join([self.structTypes[s] for s in value.extstructs])))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700921 checkExpr.append('skip |= validate_struct_pnext("{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedVulkanHeaderVersion, {});\n'.format(
Mark Lobodzinski3c828522017-06-26 13:05:57 -0600922 funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, vuid, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600923 return checkExpr
924 #
925 # Generate the pointer check string
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600926 def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600927 checkExpr = []
Mark Lobodzinskidead0b62017-06-28 13:22:03 -0600928 vuid_tag_name = struct_type_name if struct_type_name is not None else funcPrintName
Mark Lobodzinski85672672016-10-13 08:36:42 -0600929 if lenValue:
Dave Houlton413a6782018-05-22 13:01:54 -0600930 count_required_vuid = self.GetVuid(vuid_tag_name, "%s-arraylength" % (lenValue.name))
931 array_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
Tobias Hectorebb855f2019-07-23 12:17:33 +0100932 # TODO: Remove workaround for missing optional tag in vk.xml
933 if array_required_vuid == '"VUID-VkFramebufferCreateInfo-pAttachments-parameter"':
934 return []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600935 # This is an array with a pointer to a count value
936 if lenValue.ispointer:
937 # If count and array parameters are optional, there will be no validation
938 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
939 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700940 checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600941 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 -0600942 # This is an array with an integer count value
943 else:
944 # If count and array parameters are optional, there will be no validation
945 if valueRequired == 'true' or lenValueRequired == 'true':
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600946 if value.type != 'char':
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700947 checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600948 funcPrintName, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
949 else:
950 # Arrays of strings receive special processing
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700951 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 -0600952 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 -0600953 if checkExpr:
954 if lenValue and ('->' in lenValue.name):
955 # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
956 checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
957 # This is an individual struct that is not allowed to be NULL
958 elif not value.isoptional:
959 # Function pointers need a reinterpret_cast to void*
Dave Houlton413a6782018-05-22 13:01:54 -0600960 ptr_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600961 if value.type[:4] == 'PFN_':
Dave Houlton413a6782018-05-22 13:01:54 -0600962 allocator_dict = {'pfnAllocation': '"VUID-VkAllocationCallbacks-pfnAllocation-00632"',
963 'pfnReallocation': '"VUID-VkAllocationCallbacks-pfnReallocation-00633"',
964 'pfnFree': '"VUID-VkAllocationCallbacks-pfnFree-00634"',
Mark Lobodzinski02fa1972017-06-28 14:46:14 -0600965 }
966 vuid = allocator_dict.get(value.name)
967 if vuid is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600968 ptr_required_vuid = vuid
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700969 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 -0600970 else:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700971 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 +0100972 else:
973 # Special case for optional internal allocation function pointers.
974 if (value.type, value.name) == ('PFN_vkInternalAllocationNotification', 'pfnInternalAllocation'):
Ricardo Garciac52489a2019-04-02 18:15:20 +0200975 checkExpr.extend(self.internalAllocationCheck(funcPrintName, prefix, value.name, 'pfnInternalFree', postProcSpec))
976 elif (value.type, value.name) == ('PFN_vkInternalFreeNotification', 'pfnInternalFree'):
977 checkExpr.extend(self.internalAllocationCheck(funcPrintName, prefix, value.name, 'pfnInternalAllocation', postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600978 return checkExpr
Ricardo Garciac52489a2019-04-02 18:15:20 +0200979
980 #
981 # Generate internal allocation function pointer check.
982 def internalAllocationCheck(self, funcPrintName, prefix, name, complementaryName, postProcSpec):
983 checkExpr = []
984 vuid = '"VUID-VkAllocationCallbacks-pfnInternalAllocation-00635"'
985 checkExpr.append('if ({}{} != NULL)'.format(prefix, name))
986 checkExpr.append('{')
987 local_indent = self.incIndent('')
988 # Function pointers need a reinterpret_cast to void*
989 checkExpr.append(local_indent + 'skip |= validate_required_pointer("{}", {ppp}"{}{}"{pps}, reinterpret_cast<const void*>({}{}), {});\n'.format(funcPrintName, prefix, complementaryName, prefix, complementaryName, vuid, **postProcSpec))
990 checkExpr.append('}\n')
991 return checkExpr
992
Mark Lobodzinski85672672016-10-13 08:36:42 -0600993 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -0600994 # Process struct member validation code, performing name substitution if required
Mark Lobodzinski85672672016-10-13 08:36:42 -0600995 def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
996 # Build format specifier list
997 kwargs = {}
998 if '{postProcPrefix}' in line:
999 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
1000 if type(memberDisplayNamePrefix) is tuple:
1001 kwargs['postProcPrefix'] = 'ParameterName('
1002 else:
1003 kwargs['postProcPrefix'] = postProcSpec['ppp']
1004 if '{postProcSuffix}' in line:
1005 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
1006 if type(memberDisplayNamePrefix) is tuple:
1007 kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
1008 else:
1009 kwargs['postProcSuffix'] = postProcSpec['pps']
1010 if '{postProcInsert}' in line:
1011 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
1012 if type(memberDisplayNamePrefix) is tuple:
1013 kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
1014 else:
1015 kwargs['postProcInsert'] = postProcSpec['ppi']
1016 if '{funcName}' in line:
1017 kwargs['funcName'] = funcName
1018 if '{valuePrefix}' in line:
1019 kwargs['valuePrefix'] = memberNamePrefix
1020 if '{displayNamePrefix}' in line:
1021 # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
1022 if type(memberDisplayNamePrefix) is tuple:
1023 kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
1024 else:
1025 kwargs['displayNamePrefix'] = memberDisplayNamePrefix
1026
1027 if kwargs:
1028 # Need to escape the C++ curly braces
1029 if 'IndexVector' in line:
1030 line = line.replace('IndexVector{ ', 'IndexVector{{ ')
1031 line = line.replace(' }),', ' }}),')
1032 return line.format(**kwargs)
1033 return line
1034 #
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001035 # Process struct member validation code, stripping metadata
1036 def ScrubStructCode(self, code):
1037 scrubbed_lines = ''
1038 for line in code:
1039 if 'validate_struct_pnext' in line:
1040 continue
1041 if 'allowed_structs' in line:
1042 continue
1043 if 'xml-driven validation' in line:
1044 continue
1045 line = line.replace('{postProcPrefix}', '')
1046 line = line.replace('{postProcSuffix}', '')
1047 line = line.replace('{postProcInsert}', '')
1048 line = line.replace('{funcName}', '')
1049 line = line.replace('{valuePrefix}', '')
1050 line = line.replace('{displayNamePrefix}', '')
1051 line = line.replace('{IndexVector}', '')
1052 line = line.replace('local_data->', '')
1053 scrubbed_lines += line
1054 return scrubbed_lines
1055 #
Mark Lobodzinski85672672016-10-13 08:36:42 -06001056 # Process struct validation code for inclusion in function or parent struct validation code
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001057 def expandStructCode(self, item_type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
1058 lines = self.validatedStructs[item_type]
Mark Lobodzinski85672672016-10-13 08:36:42 -06001059 for line in lines:
1060 if output:
1061 output[-1] += '\n'
1062 if type(line) is list:
1063 for sub in line:
1064 output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1065 else:
1066 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1067 return output
1068 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001069 # Process struct pointer/array validation code, performing name substitution if required
Mark Lobodzinski85672672016-10-13 08:36:42 -06001070 def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
1071 expr = []
1072 expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
1073 expr.append('{')
1074 indent = self.incIndent(None)
1075 if lenValue:
1076 # Need to process all elements in the array
1077 indexName = lenValue.name.replace('Count', 'Index')
1078 expr[-1] += '\n'
Mark Young39389872017-01-19 21:10:49 -07001079 if lenValue.ispointer:
1080 # If the length value is a pointer, de-reference it for the count.
1081 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < *{}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
1082 else:
1083 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001084 expr.append(indent + '{')
1085 indent = self.incIndent(indent)
1086 # Prefix for value name to display in error message
Mark Lobodzinski6f82eb52016-12-05 07:38:41 -07001087 if value.ispointer == 2:
1088 memberNamePrefix = '{}{}[{}]->'.format(prefix, value.name, indexName)
1089 memberDisplayNamePrefix = ('{}[%i]->'.format(valueDisplayName), indexName)
1090 else:
1091 memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
1092 memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001093 else:
1094 memberNamePrefix = '{}{}->'.format(prefix, value.name)
1095 memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001096 # Expand the struct validation lines
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001097 expr = self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001098 if lenValue:
1099 # Close if and for scopes
1100 indent = self.decIndent(indent)
1101 expr.append(indent + '}\n')
1102 expr.append('}\n')
1103 return expr
1104 #
1105 # Generate the parameter checking code
1106 def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
1107 lines = [] # Generated lines of code
1108 unused = [] # Unused variable names
1109 for value in values:
1110 usedLines = []
1111 lenParam = None
1112 #
1113 # Prefix and suffix for post processing of parameter names for struct members. Arrays of structures need special processing to include the array index in the full parameter name.
1114 postProcSpec = {}
1115 postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
1116 postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
1117 postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
1118 #
1119 # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name
1120 valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
1121 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001122 # Check for NULL pointers, ignore the in-out count parameters that
Mark Lobodzinski85672672016-10-13 08:36:42 -06001123 # will be validated with their associated array
1124 if (value.ispointer or value.isstaticarray) and not value.iscount:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001125 # Parameters for function argument generation
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001126 req = 'true' # Parameter cannot be NULL
Mark Lobodzinski85672672016-10-13 08:36:42 -06001127 cpReq = 'true' # Count pointer cannot be NULL
1128 cvReq = 'true' # Count value cannot be 0
1129 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
Mark Lobodzinski85672672016-10-13 08:36:42 -06001130 # Generate required/optional parameter strings for the pointer and count values
1131 if value.isoptional:
1132 req = 'false'
1133 if value.len:
1134 # The parameter is an array with an explicit count parameter
1135 lenParam = self.getLenParam(values, value.len)
1136 lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
1137 if lenParam.ispointer:
1138 # Count parameters that are pointers are inout
1139 if type(lenParam.isoptional) is list:
1140 if lenParam.isoptional[0]:
1141 cpReq = 'false'
1142 if lenParam.isoptional[1]:
1143 cvReq = 'false'
1144 else:
1145 if lenParam.isoptional:
1146 cpReq = 'false'
1147 else:
1148 if lenParam.isoptional:
1149 cvReq = 'false'
1150 #
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001151 # The parameter will not be processed when tagged as 'noautovalidity'
Mark Lobodzinski85672672016-10-13 08:36:42 -06001152 # For the pointer to struct case, the struct pointer will not be validated, but any
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001153 # members not tagged as 'noautovalidity' will be validated
1154 # We special-case the custom allocator checks, as they are explicit but can be auto-generated.
Ricardo Garcia98ba9642019-02-19 11:25:18 +01001155 AllocatorFunctions = ['PFN_vkAllocationFunction', 'PFN_vkReallocationFunction', 'PFN_vkFreeFunction', 'PFN_vkInternalAllocationNotification', 'PFN_vkInternalFreeNotification']
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001156 if value.noautovalidity and value.type not in AllocatorFunctions:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001157 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1158 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1159 else:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001160 if value.type in self.structTypes:
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001161 # If this is a pointer to a struct with an sType field, verify the type
Mark Lobodzinski9cf24dd2017-06-28 14:23:22 -06001162 usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001163 # If this is an input handle array that is not allowed to contain NULL handles, verify that none of the handles are VK_NULL_HANDLE
1164 elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
1165 usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1166 elif value.type in self.flags and value.isconst:
1167 usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1168 elif value.isbool and value.isconst:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001169 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 -06001170 elif value.israngedenum and value.isconst:
Mark Lobodzinskiaff801e2017-07-25 15:29:57 -06001171 enum_value_list = 'All%sEnums' % value.type
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001172 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 -07001173 elif value.name == 'pNext' and value.isconst:
Mark Lobodzinski9ddf9282018-05-31 13:59:59 -06001174 usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001175 else:
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -06001176 usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001177 # 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 -06001178 if value.type in self.validatedStructs:
1179 if value.isconst: # or value.type in self.returnedonly_structs:
1180 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
1181 elif value.type in self.returnedonly_structs:
1182 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001183 # Non-pointer types
1184 else:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001185 # The parameter will not be processes when tagged as 'noautovalidity'
1186 # For the struct case, the struct type will not be validated, but any
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001187 # members not tagged as 'noautovalidity' will be validated
Mark Lobodzinski85672672016-10-13 08:36:42 -06001188 if value.noautovalidity:
1189 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1190 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1191 else:
Mark Lobodzinski024b2822017-06-27 13:22:05 -06001192 vuid_name_tag = structTypeName if structTypeName is not None else funcName
Mark Lobodzinski85672672016-10-13 08:36:42 -06001193 if value.type in self.structTypes:
1194 stype = self.structTypes[value.type]
Dave Houlton413a6782018-05-22 13:01:54 -06001195 vuid = self.GetVuid(value.type, "sType-sType")
Mark Lobodzinskia16ebc72018-06-15 14:47:39 -06001196 undefined_vuid = '"kVUIDUndefined"'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001197 usedLines.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false, kVUIDUndefined, {});\n'.format(
Mike Schuchardt24ac4e72018-08-11 17:37:20 -07001198 funcName, valueDisplayName, valuePrefix, vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001199 elif value.type in self.handleTypes:
1200 if not self.isHandleOptional(value, None):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001201 usedLines.append('skip |= validate_required_handle("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Petr Kraus52758be2019-08-12 00:53:58 +02001202 elif value.type in self.flags and value.type.replace('Flags', 'FlagBits') not in self.flagBits:
1203 vuid = self.GetVuid(vuid_name_tag, "%s-zerobitmask" % (value.name))
1204 usedLines.append('skip |= validate_reserved_flags("{}", {ppp}"{}"{pps}, {pf}{}, {});\n'.format(funcName, valueDisplayName, value.name, vuid, pf=valuePrefix, **postProcSpec))
1205 elif value.type in self.flags or value.type in self.flagBits:
1206 if value.type in self.flags:
1207 flagBitsName = value.type.replace('Flags', 'FlagBits')
1208 flagsType = 'kOptionalFlags' if value.isoptional else 'kRequiredFlags'
1209 invalidVuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
1210 zeroVuid = self.GetVuid(vuid_name_tag, "%s-requiredbitmask" % (value.name))
1211 elif value.type in self.flagBits:
1212 flagBitsName = value.type
1213 flagsType = 'kOptionalSingleBit' if value.isoptional else 'kRequiredSingleBit'
1214 invalidVuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
1215 zeroVuid = invalidVuid
1216 allFlagsName = 'All' + flagBitsName
1217
1218 invalid_vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
1219 allFlagsName = 'All' + flagBitsName
1220 zeroVuidArg = '' if value.isoptional else ', ' + zeroVuid
1221 usedLines.append('skip |= validate_flags("{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {}, {}{});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsType, invalidVuid, zeroVuidArg, pf=valuePrefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001222 elif value.isbool:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001223 usedLines.append('skip |= validate_bool32("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001224 elif value.israngedenum:
Dave Houlton413a6782018-05-22 13:01:54 -06001225 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinski74cb45f2017-07-25 15:10:29 -06001226 enum_value_list = 'All%sEnums' % value.type
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001227 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 -06001228 # If this is a struct, see if it contains members that need to be checked
1229 if value.type in self.validatedStructs:
1230 memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
1231 memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001232 usedLines.append(self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001233 # Append the parameter check to the function body for the current command
1234 if usedLines:
1235 # Apply special conditional checks
1236 if value.condition:
1237 usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
1238 lines += usedLines
1239 elif not value.iscount:
1240 # If no expression was generated for this value, it is unreferenced by the validation function, unless
1241 # it is an array count, which is indirectly referenced for array valiadation.
1242 unused.append(value.name)
Mark Lobodzinskid4950072017-08-01 13:02:20 -06001243 if not lines:
1244 lines.append('// No xml-driven validation\n')
Mark Lobodzinski85672672016-10-13 08:36:42 -06001245 return lines, unused
1246 #
1247 # Generate the struct member check code from the captured data
1248 def processStructMemberData(self):
1249 indent = self.incIndent(None)
1250 for struct in self.structMembers:
1251 #
1252 # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
1253 lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
1254 if lines:
1255 self.validatedStructs[struct.name] = lines
1256 #
1257 # Generate the command param check code from the captured data
1258 def processCmdData(self):
1259 indent = self.incIndent(None)
1260 for command in self.commands:
1261 # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
1262 startIndex = 0 if command.name == 'vkCreateInstance' else 1
1263 lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
Mark Lobodzinski3f10bfe2017-08-23 15:23:23 -06001264 # Cannot validate extension dependencies for device extension APIs having a physical device as their dispatchable object
Mike Schuchardtafd00482017-08-24 15:15:02 -06001265 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 -06001266 ext_test = ''
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001267 if command.params[0].type in ["VkInstance", "VkPhysicalDevice"] or command.name == 'vkCreateInstance':
1268 ext_table_type = 'instance'
1269 else:
1270 ext_table_type = 'device'
Mike Schuchardtafd00482017-08-24 15:15:02 -06001271 for ext in self.required_extensions[command.name]:
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001272 ext_name_define = ''
1273 ext_enable_name = ''
1274 for extension in self.registry.extensions:
1275 if extension.attrib['name'] == ext:
1276 ext_name_define = extension[0][1].get('name')
1277 ext_enable_name = ext_name_define.lower()
1278 ext_enable_name = re.sub('_extension_name', '', ext_enable_name)
1279 break
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001280 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 -06001281 lines.insert(0, ext_test)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001282 if lines:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001283 func_sig = self.getCmdDef(command) + ' {\n'
1284 func_sig = func_sig.split('VKAPI_CALL vk')[1]
1285 cmdDef = 'bool StatelessValidation::PreCallValidate' + func_sig
Mark Lobodzinskid4950072017-08-01 13:02:20 -06001286 cmdDef += '%sbool skip = false;\n' % indent
Mark Lobodzinski70d416b2019-07-08 15:59:43 -06001287 for line in lines:
1288 if type(line) is list:
1289 for sub in line:
1290 cmdDef += indent + sub
1291 else:
1292 cmdDef += indent + line
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001293 # Insert call to custom-written function if present
1294 if command.name in self.functions_with_manual_checks:
1295 # Generate parameter list for manual fcn and down-chain calls
1296 params_text = ''
1297 for param in command.params:
1298 params_text += '%s, ' % param.name
1299 params_text = params_text[:-2] + ');\n'
Petr Kraus907c3782019-08-12 03:06:46 +02001300 cmdDef += ' if (!skip) skip |= manual_PreCallValidate'+ command.name[2:] + '(' + params_text
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001301 cmdDef += '%sreturn skip;\n' % indent
Mark Lobodzinski85672672016-10-13 08:36:42 -06001302 cmdDef += '}\n'
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001303 self.validation.append(cmdDef)