blob: 3392fb597ba40d39f7febe275f597a6159a9a733 [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',
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',
Jason Macnak5c954952019-07-09 15:46:12 -0700180 'vkGetAccelerationStructureHandleNV',
181 'vkCmdBuildAccelerationStructureNV',
Tobias Hectorebb855f2019-07-23 12:17:33 +0100182 'vkCreateFramebuffer',
Jeff Bolz8125a8b2019-08-16 16:29:45 -0500183 'vkCmdSetLineStippleEXT',
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700184 ]
185
Mark Lobodzinski85672672016-10-13 08:36:42 -0600186 # Commands to ignore
187 self.blacklist = [
188 'vkGetInstanceProcAddr',
189 'vkGetDeviceProcAddr',
Mark Young6ba8abe2017-11-09 10:37:04 -0700190 'vkEnumerateInstanceVersion',
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600191 'vkEnumerateInstanceLayerProperties',
192 'vkEnumerateInstanceExtensionProperties',
193 'vkEnumerateDeviceLayerProperties',
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600194 'vkEnumerateDeviceExtensionProperties',
Mike Schuchardt21638df2019-03-16 10:52:02 -0700195 'vkGetDeviceGroupSurfacePresentModes2EXT'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600196 ]
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700197
Dustin Gravesce68f082017-03-30 15:42:16 -0600198 # Structure fields to ignore
199 self.structMemberBlacklist = { 'VkWriteDescriptorSet' : ['dstSet'] }
Mark Lobodzinski85672672016-10-13 08:36:42 -0600200 # Validation conditions for some special case struct members that are conditionally validated
201 self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
202 # Header version
203 self.headerVersion = None
204 # Internal state - accumulators for different inner block text
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600205 self.validation = [] # Text comprising the main per-api parameter validation routines
Mark Lobodzinski85672672016-10-13 08:36:42 -0600206 self.stypes = [] # Values from the VkStructureType enumeration
207 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
208 self.handleTypes = set() # Set of handle type names
209 self.commands = [] # List of CommandData records for all Vulkan commands
210 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
211 self.validatedStructs = dict() # Map of structs type names to generated validation code for that struct type
212 self.enumRanges = dict() # Map of enum name to BEGIN/END range values
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600213 self.enumValueLists = '' # String containing enumerated type map definitions
Mark Lobodzinski85672672016-10-13 08:36:42 -0600214 self.flags = set() # Map of flags typenames
215 self.flagBits = dict() # Map of flag bits typename to list of values
Chris Forbes78ea32d2016-11-28 11:14:17 +1300216 self.newFlags = set() # Map of flags typenames /defined in the current feature/
Mike Schuchardtafd00482017-08-24 15:15:02 -0600217 self.required_extensions = dict() # Dictionary of required extensions for each item in the current extension
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600218 self.extension_type = '' # Type of active feature (extension), device or instance
219 self.extension_names = dict() # Dictionary of extension names to extension name defines
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600220 self.structextends_list = [] # List of extensions which extend another struct
221 self.struct_feature_protect = dict() # Dictionary of structnames and FeatureExtraProtect strings
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600222 self.valid_vuids = set() # Set of all valid VUIDs
Dave Houlton413a6782018-05-22 13:01:54 -0600223 self.vuid_dict = dict() # VUID dictionary (from JSON)
224 self.alias_dict = dict() # Dict of cmd|struct aliases
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700225 self.header_file = False # Header file generation flag
226 self.source_file = False # Source file generation flag
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600227 self.returnedonly_structs = []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600228 # Named tuples to store struct and command data
Mark Lobodzinski85672672016-10-13 08:36:42 -0600229 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600230 'isconst', 'isoptional', 'iscount', 'noautovalidity',
231 'len', 'extstructs', 'condition', 'cdecl'])
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600232 self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl', 'extension_type', 'result'])
Mark Lobodzinski85672672016-10-13 08:36:42 -0600233 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600234
Mark Lobodzinski85672672016-10-13 08:36:42 -0600235 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600236 # Generate Copyright comment block for file
237 def GenerateCopyright(self):
238 copyright = '/* *** THIS FILE IS GENERATED - DO NOT EDIT! ***\n'
239 copyright += ' * See parameter_validation_generator.py for modifications\n'
240 copyright += ' *\n'
Mike Schuchardt840b5042019-07-11 08:11:47 -0700241 copyright += ' * Copyright (c) 2015-2019 The Khronos Group Inc.\n'
242 copyright += ' * Copyright (c) 2015-2019 LunarG, Inc.\n'
243 copyright += ' * Copyright (C) 2015-2019 Google Inc.\n'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600244 copyright += ' *\n'
245 copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
246 copyright += ' * you may not use this file except in compliance with the License.\n'
247 copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n'
248 copyright += ' * You may obtain a copy of the License at\n'
249 copyright += ' *\n'
250 copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n'
251 copyright += ' *\n'
252 copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
253 copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
254 copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
255 copyright += ' * See the License for the specific language governing permissions and\n'
256 copyright += ' * limitations under the License.\n'
257 copyright += ' *\n'
258 copyright += ' * Author: Mark Lobodzinski <mark@LunarG.com>\n'
Dave Houlton413a6782018-05-22 13:01:54 -0600259 copyright += ' * Author: Dave Houlton <daveh@LunarG.com>\n'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600260 copyright += ' */\n\n'
261 return copyright
262 #
263 # Increases the global indent variable
Mark Lobodzinski85672672016-10-13 08:36:42 -0600264 def incIndent(self, indent):
265 inc = ' ' * self.INDENT_SPACES
266 if indent:
267 return indent + inc
268 return inc
269 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600270 # Decreases the global indent variable
Mark Lobodzinski85672672016-10-13 08:36:42 -0600271 def decIndent(self, indent):
272 if indent and (len(indent) > self.INDENT_SPACES):
273 return indent[:-self.INDENT_SPACES]
274 return ''
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600275 #
Dave Houlton413a6782018-05-22 13:01:54 -0600276 # Walk the JSON-derived dict and find all "vuid" key values
277 def ExtractVUIDs(self, d):
278 if hasattr(d, 'items'):
279 for k, v in d.items():
280 if k == "vuid":
281 yield v
282 elif isinstance(v, dict):
283 for s in self.ExtractVUIDs(v):
284 yield s
285 elif isinstance (v, list):
286 for l in v:
287 for s in self.ExtractVUIDs(l):
288 yield s
Mark Lobodzinski85672672016-10-13 08:36:42 -0600289 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600290 # Called at file creation time
Mark Lobodzinski85672672016-10-13 08:36:42 -0600291 def beginFile(self, genOpts):
292 OutputGenerator.beginFile(self, genOpts)
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700293 self.header_file = (genOpts.filename == 'parameter_validation.h')
294 self.source_file = (genOpts.filename == 'parameter_validation.cpp')
295
296 if not self.header_file and not self.source_file:
297 print("Error: Output Filenames have changed, update generator source.\n")
298 sys.exit(1)
299
300 if self.source_file or self.header_file:
301 # Output Copyright text
302 s = self.GenerateCopyright()
303 write(s, file=self.outFile)
304
305 if self.header_file:
306 return
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -0600307
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700308 # Build map of structure type names to VkStructureType enum values
309 # Find all types of category "struct"
310 for struct in self.registry.tree.iterfind('types/type[@category="struct"]'):
311 # Check if struct has member named "sType" of type "VkStructureType" which has values defined
312 stype = struct.find('member[name="sType"][type="VkStructureType"][@values]')
Shahbaz Youssefi23aee922019-01-11 14:04:49 -0500313 if stype is not None:
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700314 # Store VkStructureType value for this type
315 self.structTypes[struct.get('name')] = stype.get('values')
316
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -0600317 self.valid_usage_path = genOpts.valid_usage_path
318 vu_json_filename = os.path.join(self.valid_usage_path + os.sep, 'validusage.json')
319 if os.path.isfile(vu_json_filename):
320 json_file = open(vu_json_filename, 'r')
321 self.vuid_dict = json.load(json_file)
322 json_file.close()
323 if len(self.vuid_dict) == 0:
324 print("Error: Could not find, or error loading %s/validusage.json\n", vu_json_filename)
325 sys.exit(1)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600326 #
Dave Houlton413a6782018-05-22 13:01:54 -0600327 # Build a set of all vuid text strings found in validusage.json
328 for json_vuid_string in self.ExtractVUIDs(self.vuid_dict):
329 self.valid_vuids.add(json_vuid_string)
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600330 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600331 # Headers
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700332 write('#include "chassis.h"', file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600333 self.newline()
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700334 write('#include "stateless_validation.h"', file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600335 self.newline()
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600336 #
337 # Called at end-time for final content output
Mark Lobodzinski85672672016-10-13 08:36:42 -0600338 def endFile(self):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700339 if self.source_file:
340 # C-specific
341 self.newline()
342 write(self.enumValueLists, file=self.outFile)
343 self.newline()
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600344
Locke6b6b7382019-04-16 15:08:49 -0600345 pnext_handler = 'bool StatelessValidation::ValidatePnextStructContents(const char *api_name, const ParameterName &parameter_name, const VkBaseOutStructure* header) {\n'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700346 pnext_handler += ' bool skip = false;\n'
347 pnext_handler += ' switch(header->sType) {\n'
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600348
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700349 # Do some processing here to extract data from validatedstructs...
350 for item in self.structextends_list:
351 postProcSpec = {}
352 postProcSpec['ppp'] = '' if not item else '{postProcPrefix}'
353 postProcSpec['pps'] = '' if not item else '{postProcSuffix}'
354 postProcSpec['ppi'] = '' if not item else '{postProcInsert}'
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600355
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700356 pnext_case = '\n'
357 protect = ''
358 # Guard struct cases with feature ifdefs, if necessary
359 if item in self.struct_feature_protect.keys():
360 protect = self.struct_feature_protect[item]
361 pnext_case += '#ifdef %s\n' % protect
362 pnext_case += ' // Validation code for %s structure members\n' % item
363 pnext_case += ' case %s: {\n' % self.structTypes[item]
364 pnext_case += ' %s *structure = (%s *) header;\n' % (item, item)
365 expr = self.expandStructCode(item, item, 'structure->', '', ' ', [], postProcSpec)
366 struct_validation_source = self.ScrubStructCode(expr)
367 pnext_case += '%s' % struct_validation_source
368 pnext_case += ' } break;\n'
Raul Tambre7b300182019-05-04 11:25:14 +0300369 if protect:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700370 pnext_case += '#endif // %s\n' % protect
371 # Skip functions containing no validation
Raul Tambre7b300182019-05-04 11:25:14 +0300372 if struct_validation_source:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700373 pnext_handler += pnext_case;
374 pnext_handler += ' default:\n'
375 pnext_handler += ' skip = false;\n'
376 pnext_handler += ' }\n'
377 pnext_handler += ' return skip;\n'
378 pnext_handler += '}\n'
379 write(pnext_handler, file=self.outFile)
380 self.newline()
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600381
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700382 ext_template = 'bool StatelessValidation::OutputExtensionError(const std::string &api_name, const std::string &extension_name) {\n'
383 ext_template += ' return log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,\n'
384 ext_template += ' kVUID_PVError_ExtensionNotEnabled, "Attemped to call %s() but its required extension %s has not been enabled\\n",\n'
385 ext_template += ' api_name.c_str(), extension_name.c_str());\n'
386 ext_template += '}\n'
387 write(ext_template, file=self.outFile)
388 self.newline()
389 commands_text = '\n'.join(self.validation)
390 write(commands_text, file=self.outFile)
391 self.newline()
392 if self.header_file:
393 # Output declarations and record intercepted procedures
394 write('\n'.join(self.declarations), file=self.outFile)
395 # Finish processing in superclass
396 OutputGenerator.endFile(self)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600397 #
398 # Processing at beginning of each feature or extension
Mark Lobodzinski85672672016-10-13 08:36:42 -0600399 def beginFeature(self, interface, emit):
400 # Start processing in superclass
401 OutputGenerator.beginFeature(self, interface, emit)
402 # C-specific
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600403 # Accumulate includes, defines, types, enums, function pointer typedefs, end function prototypes separately for this
404 # feature. They're only printed in endFeature().
Mark Lobodzinski85672672016-10-13 08:36:42 -0600405 self.headerVersion = None
Mark Lobodzinski85672672016-10-13 08:36:42 -0600406 self.stypes = []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600407 self.commands = []
408 self.structMembers = []
Chris Forbes78ea32d2016-11-28 11:14:17 +1300409 self.newFlags = set()
Mark Lobodzinski62f71562017-10-24 13:41:18 -0600410 self.featureExtraProtect = GetFeatureProtect(interface)
Mike Schuchardtafd00482017-08-24 15:15:02 -0600411 # Get base list of extension dependencies for all items in this extension
412 base_required_extensions = []
Mark Lobodzinski31964ca2017-09-18 14:15:09 -0600413 if "VK_VERSION_1" not in self.featureName:
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600414 # Save Name Define to get correct enable name later
415 nameElem = interface[0][1]
416 name = nameElem.get('name')
417 self.extension_names[self.featureName] = name
418 # This extension is the first dependency for this command
Mike Schuchardtafd00482017-08-24 15:15:02 -0600419 base_required_extensions.append(self.featureName)
420 # Add any defined extension dependencies to the base dependency list for this extension
421 requires = interface.get('requires')
422 if requires is not None:
423 base_required_extensions.extend(requires.split(','))
Mike Schuchardtafd00482017-08-24 15:15:02 -0600424 # Build dictionary of extension dependencies for each item in this extension
425 self.required_extensions = dict()
426 for require_element in interface.findall('require'):
427 # Copy base extension dependency list
428 required_extensions = list(base_required_extensions)
429 # Add any additional extension dependencies specified in this require block
430 additional_extensions = require_element.get('extension')
431 if additional_extensions:
432 required_extensions.extend(additional_extensions.split(','))
433 # Save full extension list for all named items
434 for element in require_element.findall('*[@name]'):
435 self.required_extensions[element.get('name')] = required_extensions
436
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600437 # And note if this is an Instance or Device extension
438 self.extension_type = interface.get('type')
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600439 #
440 # Called at the end of each extension (feature)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600441 def endFeature(self):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700442 if self.header_file:
443 return
Mark Lobodzinski85672672016-10-13 08:36:42 -0600444 # C-specific
445 # Actually write the interface to the output file.
446 if (self.emit):
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600447 # 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 -0600448 # or move it below the 'for section...' loop.
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600449 ifdef = ''
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100450 if (self.featureExtraProtect is not None):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600451 ifdef = '#ifdef %s\n' % self.featureExtraProtect
452 self.validation.append(ifdef)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600453 # Generate the struct member checking code from the captured data
454 self.processStructMemberData()
455 # Generate the command parameter checking code from the captured data
456 self.processCmdData()
457 # Write the declaration for the HeaderVersion
458 if self.headerVersion:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700459 write('const uint32_t GeneratedVulkanHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600460 self.newline()
461 # Write the declarations for the VkFlags values combining all flag bits
Chris Forbes78ea32d2016-11-28 11:14:17 +1300462 for flag in sorted(self.newFlags):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600463 flagBits = flag.replace('Flags', 'FlagBits')
464 if flagBits in self.flagBits:
465 bits = self.flagBits[flagBits]
466 decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
467 for bit in bits[1:]:
468 decl += '|' + bit
469 decl += ';'
470 write(decl, file=self.outFile)
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600471 endif = '\n'
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100472 if (self.featureExtraProtect is not None):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600473 endif = '#endif // %s\n' % self.featureExtraProtect
474 self.validation.append(endif)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600475 # Finish processing in superclass
476 OutputGenerator.endFeature(self)
477 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600478 # Type generation
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700479 def genType(self, typeinfo, name, alias):
Dave Houlton413a6782018-05-22 13:01:54 -0600480 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100481 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600482 self.alias_dict[name]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700483 OutputGenerator.genType(self, typeinfo, name, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600484 typeElem = typeinfo.elem
Mark Lobodzinski87017df2018-05-30 11:29:24 -0600485 # 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 -0600486 category = typeElem.get('category')
487 if (category == 'struct' or category == 'union'):
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700488 self.genStruct(typeinfo, name, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600489 elif (category == 'handle'):
490 self.handleTypes.add(name)
491 elif (category == 'bitmask'):
492 self.flags.add(name)
Chris Forbes78ea32d2016-11-28 11:14:17 +1300493 self.newFlags.add(name)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600494 elif (category == 'define'):
495 if name == 'VK_HEADER_VERSION':
496 nameElem = typeElem.find('name')
497 self.headerVersion = noneStr(nameElem.tail).strip()
498 #
499 # Struct parameter check generation.
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600500 # This is a special case of the <type> tag where the contents are interpreted as a set of <member> tags instead of freeform C
501 # type declarations. The <member> tags are just like <param> tags - they are a declaration of a struct or union member.
502 # Only simple member declarations are supported (no nested structs etc.)
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700503 def genStruct(self, typeinfo, typeName, alias):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700504 if not self.source_file:
505 return
Dave Houlton413a6782018-05-22 13:01:54 -0600506 # alias has already been recorded in genType, above
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700507 OutputGenerator.genStruct(self, typeinfo, typeName, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600508 conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
509 members = typeinfo.elem.findall('.//member')
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600510 if self.featureExtraProtect is not None:
511 self.struct_feature_protect[typeName] = self.featureExtraProtect
Mark Lobodzinski85672672016-10-13 08:36:42 -0600512 #
513 # Iterate over members once to get length parameters for arrays
514 lens = set()
515 for member in members:
516 len = self.getLen(member)
517 if len:
518 lens.add(len)
519 #
520 # Generate member info
521 membersInfo = []
522 for member in members:
523 # Get the member's type and name
524 info = self.getTypeNameTuple(member)
525 type = info[0]
526 name = info[1]
527 stypeValue = ''
528 cdecl = self.makeCParamDecl(member, 0)
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700529
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600530 # Store pointer/array/string info -- Check for parameter name in lens set
Mark Lobodzinski85672672016-10-13 08:36:42 -0600531 iscount = False
532 if name in lens:
533 iscount = True
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600534 # The pNext members are not tagged as optional, but are treated as optional for parameter NULL checks. Static array
535 # members are also treated as optional to skip NULL pointer validation, as they won't be NULL.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600536 isstaticarray = self.paramIsStaticArray(member)
537 isoptional = False
538 if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
539 isoptional = True
Dustin Gravesce68f082017-03-30 15:42:16 -0600540 # Determine if value should be ignored by code generation.
541 noautovalidity = False
542 if (member.attrib.get('noautovalidity') is not None) or ((typeName in self.structMemberBlacklist) and (name in self.structMemberBlacklist[typeName])):
543 noautovalidity = True
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600544 structextends = False
Mark Lobodzinski85672672016-10-13 08:36:42 -0600545 membersInfo.append(self.CommandParam(type=type, name=name,
546 ispointer=self.paramIsPointer(member),
547 isstaticarray=isstaticarray,
548 isbool=True if type == 'VkBool32' else False,
549 israngedenum=True if type in self.enumRanges else False,
550 isconst=True if 'const' in cdecl else False,
551 isoptional=isoptional,
552 iscount=iscount,
Dustin Gravesce68f082017-03-30 15:42:16 -0600553 noautovalidity=noautovalidity,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600554 len=self.getLen(member),
Mike Schuchardta40d0b02017-07-23 12:47:47 -0600555 extstructs=self.registry.validextensionstructs[typeName] if name == 'pNext' else None,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600556 condition=conditions[name] if conditions and name in conditions else None,
557 cdecl=cdecl))
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600558 # If this struct extends another, keep its name in list for further processing
559 if typeinfo.elem.attrib.get('structextends') is not None:
560 self.structextends_list.append(typeName)
561 # Returnedonly structs should have most of their members ignored -- on entry, we only care about validating the sType and
562 # pNext members. Everything else will be overwritten by the callee.
563 if typeinfo.elem.attrib.get('returnedonly') is not None:
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600564 self.returnedonly_structs.append(typeName)
565 membersInfo = [m for m in membersInfo if m.name in ('sType', 'pNext')]
Mark Lobodzinski85672672016-10-13 08:36:42 -0600566 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
567 #
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600568 # Capture group (e.g. C "enum" type) info to be used for param check code generation.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600569 # These are concatenated together with other types.
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700570 def genGroup(self, groupinfo, groupName, alias):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700571 if not self.source_file:
572 return
Dave Houlton413a6782018-05-22 13:01:54 -0600573 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100574 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600575 self.alias_dict[groupName]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700576 OutputGenerator.genGroup(self, groupinfo, groupName, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600577 groupElem = groupinfo.elem
Mark Lobodzinski85672672016-10-13 08:36:42 -0600578 # Store the sType values
579 if groupName == 'VkStructureType':
580 for elem in groupElem.findall('enum'):
581 self.stypes.append(elem.get('name'))
582 elif 'FlagBits' in groupName:
583 bits = []
584 for elem in groupElem.findall('enum'):
Shannon McPherson533a66c2018-08-21 12:09:25 -0600585 if elem.get('supported') != 'disabled':
586 bits.append(elem.get('name'))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600587 if bits:
588 self.flagBits[groupName] = bits
589 else:
590 # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
591 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
592 expandPrefix = expandName
593 expandSuffix = ''
594 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
595 if expandSuffixMatch:
596 expandSuffix = '_' + expandSuffixMatch.group()
597 # Strip off the suffix from the prefix
598 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
599 isEnum = ('FLAG_BITS' not in expandPrefix)
600 if isEnum:
601 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600602 # Create definition for a list containing valid enum values for this enumerated type
Mike Schuchardt21638df2019-03-16 10:52:02 -0700603 if self.featureExtraProtect is not None:
604 enum_entry = '\n#ifdef %s\n' % self.featureExtraProtect
605 else:
606 enum_entry = ''
607 enum_entry += 'const std::vector<%s> All%sEnums = {' % (groupName, groupName)
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600608 for enum in groupElem:
609 name = enum.get('name')
Mark Lobodzinski117d88f2017-07-27 12:09:08 -0600610 if name is not None and enum.get('supported') != 'disabled':
611 enum_entry += '%s, ' % name
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600612 enum_entry += '};\n'
Mike Schuchardt21638df2019-03-16 10:52:02 -0700613 if self.featureExtraProtect is not None:
Jason Macnak49f32c32019-07-29 14:38:44 -0700614 enum_entry += '#endif // %s\n' % self.featureExtraProtect
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600615 self.enumValueLists += enum_entry
Mark Lobodzinski85672672016-10-13 08:36:42 -0600616 #
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600617 # Capture command parameter info to be used for param check code generation.
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700618 def genCmd(self, cmdinfo, name, alias):
Dave Houlton413a6782018-05-22 13:01:54 -0600619 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100620 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600621 self.alias_dict[name]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700622 OutputGenerator.genCmd(self, cmdinfo, name, alias)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600623 decls = self.makeCDecls(cmdinfo.elem)
624 typedef = decls[1]
625 typedef = typedef.split(')',1)[1]
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700626 if self.header_file:
627 if name not in self.blacklist:
628 if (self.featureExtraProtect is not None):
629 self.declarations += [ '#ifdef %s' % self.featureExtraProtect ]
630 # Strip off 'vk' from API name
631 self.declarations += [ '%s%s' % ('bool PreCallValidate', decls[0].split("VKAPI_CALL vk")[1])]
632 if (self.featureExtraProtect is not None):
633 self.declarations += [ '#endif' ]
634 if self.source_file:
635 if name not in self.blacklist:
636 params = cmdinfo.elem.findall('param')
637 # Get list of array lengths
638 lens = set()
639 for param in params:
640 len = self.getLen(param)
641 if len:
642 lens.add(len)
643 # Get param info
644 paramsInfo = []
645 for param in params:
646 paramInfo = self.getTypeNameTuple(param)
647 cdecl = self.makeCParamDecl(param, 0)
648 # Check for parameter name in lens set
649 iscount = False
650 if paramInfo[1] in lens:
651 iscount = True
652 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
653 ispointer=self.paramIsPointer(param),
654 isstaticarray=self.paramIsStaticArray(param),
655 isbool=True if paramInfo[0] == 'VkBool32' else False,
656 israngedenum=True if paramInfo[0] in self.enumRanges else False,
657 isconst=True if 'const' in cdecl else False,
658 isoptional=self.paramIsOptional(param),
659 iscount=iscount,
660 noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
661 len=self.getLen(param),
662 extstructs=None,
663 condition=None,
664 cdecl=cdecl))
665 # Save return value information, if any
666 result_type = ''
667 resultinfo = cmdinfo.elem.find('proto/type')
668 if (resultinfo is not None and resultinfo.text != 'void'):
669 result_type = resultinfo.text
670 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 -0600671 #
672 # Check if the parameter passed in is a pointer
673 def paramIsPointer(self, param):
674 ispointer = 0
675 paramtype = param.find('type')
676 if (paramtype.tail is not None) and ('*' in paramtype.tail):
677 ispointer = paramtype.tail.count('*')
678 elif paramtype.text[:4] == 'PFN_':
679 # Treat function pointer typedefs as a pointer to a single value
680 ispointer = 1
681 return ispointer
682 #
683 # Check if the parameter passed in is a static array
684 def paramIsStaticArray(self, param):
685 isstaticarray = 0
686 paramname = param.find('name')
687 if (paramname.tail is not None) and ('[' in paramname.tail):
688 isstaticarray = paramname.tail.count('[')
689 return isstaticarray
690 #
691 # Check if the parameter passed in is optional
692 # Returns a list of Boolean values for comma separated len attributes (len='false,true')
693 def paramIsOptional(self, param):
694 # See if the handle is optional
695 isoptional = False
696 # Simple, if it's optional, return true
697 optString = param.attrib.get('optional')
698 if optString:
699 if optString == 'true':
700 isoptional = True
701 elif ',' in optString:
702 opts = []
703 for opt in optString.split(','):
704 val = opt.strip()
705 if val == 'true':
706 opts.append(True)
707 elif val == 'false':
708 opts.append(False)
709 else:
710 print('Unrecognized len attribute value',val)
711 isoptional = opts
712 return isoptional
713 #
714 # Check if the handle passed in is optional
715 # Uses the same logic as ValidityOutputGenerator.isHandleOptional
716 def isHandleOptional(self, param, lenParam):
717 # Simple, if it's optional, return true
718 if param.isoptional:
719 return True
720 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
721 if param.noautovalidity:
722 return True
723 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
724 if lenParam and lenParam.isoptional:
725 return True
726 return False
727 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600728 # Retrieve the value of the len tag
729 def getLen(self, param):
730 result = None
731 len = param.attrib.get('len')
732 if len and len != 'null-terminated':
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600733 # For string arrays, 'len' can look like 'count,null-terminated', indicating that we have a null terminated array of
734 # 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 -0600735 if 'null-terminated' in len:
736 result = len.split(',')[0]
737 else:
738 result = len
739 result = str(result).replace('::', '->')
740 return result
741 #
742 # Retrieve the type and name for a parameter
743 def getTypeNameTuple(self, param):
744 type = ''
745 name = ''
746 for elem in param:
747 if elem.tag == 'type':
748 type = noneStr(elem.text)
749 elif elem.tag == 'name':
750 name = noneStr(elem.text)
751 return (type, name)
752 #
753 # Find a named parameter in a parameter list
754 def getParamByName(self, params, name):
755 for param in params:
756 if param.name == name:
757 return param
758 return None
759 #
760 # Extract length values from latexmath. Currently an inflexible solution that looks for specific
761 # patterns that are found in vk.xml. Will need to be updated when new patterns are introduced.
762 def parseLateXMath(self, source):
763 name = 'ERROR'
764 decoratedName = 'ERROR'
765 if 'mathit' in source:
Mark Lobodzinski36c33862017-02-13 10:15:53 -0700766 # Matches expressions similar to 'latexmath:[\lceil{\mathit{rasterizationSamples} \over 32}\rceil]'
767 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 -0600768 if not match or match.group(1) != match.group(4):
769 raise 'Unrecognized latexmath expression'
770 name = match.group(2)
771 decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
772 else:
Mark Lobodzinski36c33862017-02-13 10:15:53 -0700773 # Matches expressions similar to 'latexmath : [dataSize \over 4]'
Shannon McPhersonbd68df02018-10-29 15:04:41 -0600774 match = re.match(r'latexmath\s*\:\s*\[\s*(\\textrm\{)?(\w+)\}?\s*\\over\s*(\d+)\s*\]', source)
775 name = match.group(2)
776 decoratedName = '{}/{}'.format(*match.group(2, 3))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600777 return name, decoratedName
778 #
779 # Get the length paramater record for the specified parameter name
780 def getLenParam(self, params, name):
781 lenParam = None
782 if name:
783 if '->' in name:
784 # The count is obtained by dereferencing a member of a struct parameter
785 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600786 isstaticarray=None, isoptional=False, type=None, noautovalidity=False,
787 len=None, extstructs=None, condition=None, cdecl=None)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600788 elif 'latexmath' in name:
789 lenName, decoratedName = self.parseLateXMath(name)
790 lenParam = self.getParamByName(params, lenName)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600791 else:
792 lenParam = self.getParamByName(params, name)
793 return lenParam
794 #
795 # Convert a vulkan.h command declaration into a parameter_validation.h definition
796 def getCmdDef(self, cmd):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600797 # Strip the trailing ';' and split into individual lines
798 lines = cmd.cdecl[:-1].split('\n')
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600799 cmd_hdr = '\n'.join(lines)
800 return cmd_hdr
Mark Lobodzinski85672672016-10-13 08:36:42 -0600801 #
802 # Generate the code to check for a NULL dereference before calling the
803 # validation function
804 def genCheckedLengthCall(self, name, exprs):
805 count = name.count('->')
806 if count:
807 checkedExpr = []
808 localIndent = ''
809 elements = name.split('->')
810 # Open the if expression blocks
811 for i in range(0, count):
812 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
813 localIndent = self.incIndent(localIndent)
814 # Add the validation expression
815 for expr in exprs:
816 checkedExpr.append(localIndent + expr)
817 # Close the if blocks
818 for i in range(0, count):
819 localIndent = self.decIndent(localIndent)
820 checkedExpr.append(localIndent + '}\n')
821 return [checkedExpr]
822 # No if statements were required
823 return exprs
824 #
825 # Generate code to check for a specific condition before executing validation code
826 def genConditionalCall(self, prefix, condition, exprs):
827 checkedExpr = []
828 localIndent = ''
829 formattedCondition = condition.format(prefix)
830 checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
831 checkedExpr.append(localIndent + '{\n')
832 localIndent = self.incIndent(localIndent)
833 for expr in exprs:
834 checkedExpr.append(localIndent + expr)
835 localIndent = self.decIndent(localIndent)
836 checkedExpr.append(localIndent + '}\n')
837 return [checkedExpr]
838 #
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600839 # Get VUID identifier from implicit VUID tag
Dave Houlton413a6782018-05-22 13:01:54 -0600840 def GetVuid(self, name, suffix):
841 vuid_string = 'VUID-%s-%s' % (name, suffix)
842 vuid = "kVUIDUndefined"
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600843 if '->' in vuid_string:
Dave Houlton413a6782018-05-22 13:01:54 -0600844 return vuid
845 if vuid_string in self.valid_vuids:
846 vuid = "\"%s\"" % vuid_string
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600847 else:
Dave Houlton413a6782018-05-22 13:01:54 -0600848 if name in self.alias_dict:
849 alias_string = 'VUID-%s-%s' % (self.alias_dict[name], suffix)
850 if alias_string in self.valid_vuids:
Shannon McPherson23d97212019-02-18 13:39:42 -0700851 vuid = "\"%s\"" % alias_string
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600852 return vuid
853 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600854 # Generate the sType check string
Mark Lobodzinski9cf24dd2017-06-28 14:23:22 -0600855 def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600856 checkExpr = []
857 stype = self.structTypes[value.type]
Mark Lobodzinski59603552018-05-29 16:14:59 -0600858 vuid_name = struct_type_name if struct_type_name is not None else funcPrintName
859 stype_vuid = self.GetVuid(value.type, "sType-sType")
860 param_vuid = self.GetVuid(vuid_name, "%s-parameter" % value.name)
861
Mark Lobodzinski85672672016-10-13 08:36:42 -0600862 if lenValue:
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800863 count_required_vuid = self.GetVuid(vuid_name, "%s-arraylength" % lenValue.name)
864
Mark Lobodzinski85672672016-10-13 08:36:42 -0600865 # This is an array with a pointer to a count value
866 if lenValue.ispointer:
867 # 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 -0800868 checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {}, {});\n'.format(
869 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 -0600870 # This is an array with an integer count value
871 else:
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800872 checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {});\n'.format(
873 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 -0600874 # This is an individual struct
875 else:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700876 checkExpr.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {}, {}, {});\n'.format(
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700877 funcPrintName, valuePrintName, prefix, valueRequired, param_vuid, stype_vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600878 return checkExpr
879 #
880 # Generate the handle check string
881 def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
882 checkExpr = []
883 if lenValue:
884 if lenValue.ispointer:
885 # This is assumed to be an output array with a pointer to a count value
886 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
887 else:
888 # This is an array with an integer count value
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700889 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 -0600890 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
891 else:
892 # This is assumed to be an output handle pointer
893 raise('Unsupported parameter validation case: Output handles are not NULL checked')
894 return checkExpr
895 #
896 # Generate check string for an array of VkFlags values
897 def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
898 checkExpr = []
899 flagBitsName = value.type.replace('Flags', 'FlagBits')
900 if not flagBitsName in self.flagBits:
901 raise('Unsupported parameter validation case: array of reserved VkFlags')
902 else:
903 allFlags = 'All' + flagBitsName
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700904 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 -0600905 return checkExpr
906 #
907 # Generate pNext check string
Mark Lobodzinski3c828522017-06-26 13:05:57 -0600908 def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600909 checkExpr = []
910 # Generate an array of acceptable VkStructureType values for pNext
911 extStructCount = 0
912 extStructVar = 'NULL'
913 extStructNames = 'NULL'
Dave Houlton413a6782018-05-22 13:01:54 -0600914 vuid = self.GetVuid(struct_type_name, "pNext-pNext")
Mark Lobodzinski85672672016-10-13 08:36:42 -0600915 if value.extstructs:
Mike Schuchardtc73d07e2017-07-12 10:10:01 -0600916 extStructVar = 'allowed_structs_{}'.format(struct_type_name)
917 extStructCount = 'ARRAY_SIZE({})'.format(extStructVar)
Mike Schuchardta40d0b02017-07-23 12:47:47 -0600918 extStructNames = '"' + ', '.join(value.extstructs) + '"'
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700919 checkExpr.append('const VkStructureType {}[] = {{ {} }};\n'.format(extStructVar, ', '.join([self.structTypes[s] for s in value.extstructs])))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700920 checkExpr.append('skip |= validate_struct_pnext("{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedVulkanHeaderVersion, {});\n'.format(
Mark Lobodzinski3c828522017-06-26 13:05:57 -0600921 funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, vuid, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600922 return checkExpr
923 #
924 # Generate the pointer check string
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600925 def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600926 checkExpr = []
Mark Lobodzinskidead0b62017-06-28 13:22:03 -0600927 vuid_tag_name = struct_type_name if struct_type_name is not None else funcPrintName
Mark Lobodzinski85672672016-10-13 08:36:42 -0600928 if lenValue:
Dave Houlton413a6782018-05-22 13:01:54 -0600929 count_required_vuid = self.GetVuid(vuid_tag_name, "%s-arraylength" % (lenValue.name))
930 array_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
Tobias Hectorebb855f2019-07-23 12:17:33 +0100931 # TODO: Remove workaround for missing optional tag in vk.xml
932 if array_required_vuid == '"VUID-VkFramebufferCreateInfo-pAttachments-parameter"':
933 return []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600934 # This is an array with a pointer to a count value
935 if lenValue.ispointer:
936 # If count and array parameters are optional, there will be no validation
937 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
938 # 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 -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, 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 -0600941 # This is an array with an integer count value
942 else:
943 # If count and array parameters are optional, there will be no validation
944 if valueRequired == 'true' or lenValueRequired == 'true':
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600945 if value.type != 'char':
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700946 checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600947 funcPrintName, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
948 else:
949 # Arrays of strings receive special processing
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700950 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 -0600951 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 -0600952 if checkExpr:
953 if lenValue and ('->' in lenValue.name):
954 # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
955 checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
956 # This is an individual struct that is not allowed to be NULL
957 elif not value.isoptional:
958 # Function pointers need a reinterpret_cast to void*
Dave Houlton413a6782018-05-22 13:01:54 -0600959 ptr_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600960 if value.type[:4] == 'PFN_':
Dave Houlton413a6782018-05-22 13:01:54 -0600961 allocator_dict = {'pfnAllocation': '"VUID-VkAllocationCallbacks-pfnAllocation-00632"',
962 'pfnReallocation': '"VUID-VkAllocationCallbacks-pfnReallocation-00633"',
963 'pfnFree': '"VUID-VkAllocationCallbacks-pfnFree-00634"',
Mark Lobodzinski02fa1972017-06-28 14:46:14 -0600964 }
965 vuid = allocator_dict.get(value.name)
966 if vuid is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600967 ptr_required_vuid = vuid
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700968 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 -0600969 else:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700970 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 +0100971 else:
972 # Special case for optional internal allocation function pointers.
973 if (value.type, value.name) == ('PFN_vkInternalAllocationNotification', 'pfnInternalAllocation'):
Ricardo Garciac52489a2019-04-02 18:15:20 +0200974 checkExpr.extend(self.internalAllocationCheck(funcPrintName, prefix, value.name, 'pfnInternalFree', postProcSpec))
975 elif (value.type, value.name) == ('PFN_vkInternalFreeNotification', 'pfnInternalFree'):
976 checkExpr.extend(self.internalAllocationCheck(funcPrintName, prefix, value.name, 'pfnInternalAllocation', postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600977 return checkExpr
Ricardo Garciac52489a2019-04-02 18:15:20 +0200978
979 #
980 # Generate internal allocation function pointer check.
981 def internalAllocationCheck(self, funcPrintName, prefix, name, complementaryName, postProcSpec):
982 checkExpr = []
983 vuid = '"VUID-VkAllocationCallbacks-pfnInternalAllocation-00635"'
984 checkExpr.append('if ({}{} != NULL)'.format(prefix, name))
985 checkExpr.append('{')
986 local_indent = self.incIndent('')
987 # Function pointers need a reinterpret_cast to void*
988 checkExpr.append(local_indent + 'skip |= validate_required_pointer("{}", {ppp}"{}{}"{pps}, reinterpret_cast<const void*>({}{}), {});\n'.format(funcPrintName, prefix, complementaryName, prefix, complementaryName, vuid, **postProcSpec))
989 checkExpr.append('}\n')
990 return checkExpr
991
Mark Lobodzinski85672672016-10-13 08:36:42 -0600992 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -0600993 # Process struct member validation code, performing name substitution if required
Mark Lobodzinski85672672016-10-13 08:36:42 -0600994 def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
995 # Build format specifier list
996 kwargs = {}
997 if '{postProcPrefix}' in line:
998 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
999 if type(memberDisplayNamePrefix) is tuple:
1000 kwargs['postProcPrefix'] = 'ParameterName('
1001 else:
1002 kwargs['postProcPrefix'] = postProcSpec['ppp']
1003 if '{postProcSuffix}' in line:
1004 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
1005 if type(memberDisplayNamePrefix) is tuple:
1006 kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
1007 else:
1008 kwargs['postProcSuffix'] = postProcSpec['pps']
1009 if '{postProcInsert}' in line:
1010 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
1011 if type(memberDisplayNamePrefix) is tuple:
1012 kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
1013 else:
1014 kwargs['postProcInsert'] = postProcSpec['ppi']
1015 if '{funcName}' in line:
1016 kwargs['funcName'] = funcName
1017 if '{valuePrefix}' in line:
1018 kwargs['valuePrefix'] = memberNamePrefix
1019 if '{displayNamePrefix}' in line:
1020 # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
1021 if type(memberDisplayNamePrefix) is tuple:
1022 kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
1023 else:
1024 kwargs['displayNamePrefix'] = memberDisplayNamePrefix
1025
1026 if kwargs:
1027 # Need to escape the C++ curly braces
1028 if 'IndexVector' in line:
1029 line = line.replace('IndexVector{ ', 'IndexVector{{ ')
1030 line = line.replace(' }),', ' }}),')
1031 return line.format(**kwargs)
1032 return line
1033 #
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001034 # Process struct member validation code, stripping metadata
1035 def ScrubStructCode(self, code):
1036 scrubbed_lines = ''
1037 for line in code:
1038 if 'validate_struct_pnext' in line:
1039 continue
1040 if 'allowed_structs' in line:
1041 continue
1042 if 'xml-driven validation' in line:
1043 continue
1044 line = line.replace('{postProcPrefix}', '')
1045 line = line.replace('{postProcSuffix}', '')
1046 line = line.replace('{postProcInsert}', '')
1047 line = line.replace('{funcName}', '')
1048 line = line.replace('{valuePrefix}', '')
1049 line = line.replace('{displayNamePrefix}', '')
1050 line = line.replace('{IndexVector}', '')
1051 line = line.replace('local_data->', '')
1052 scrubbed_lines += line
1053 return scrubbed_lines
1054 #
Mark Lobodzinski85672672016-10-13 08:36:42 -06001055 # Process struct validation code for inclusion in function or parent struct validation code
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001056 def expandStructCode(self, item_type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
1057 lines = self.validatedStructs[item_type]
Mark Lobodzinski85672672016-10-13 08:36:42 -06001058 for line in lines:
1059 if output:
1060 output[-1] += '\n'
1061 if type(line) is list:
1062 for sub in line:
1063 output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1064 else:
1065 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1066 return output
1067 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001068 # Process struct pointer/array validation code, performing name substitution if required
Mark Lobodzinski85672672016-10-13 08:36:42 -06001069 def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
1070 expr = []
1071 expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
1072 expr.append('{')
1073 indent = self.incIndent(None)
1074 if lenValue:
1075 # Need to process all elements in the array
1076 indexName = lenValue.name.replace('Count', 'Index')
1077 expr[-1] += '\n'
Mark Young39389872017-01-19 21:10:49 -07001078 if lenValue.ispointer:
1079 # If the length value is a pointer, de-reference it for the count.
1080 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < *{}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
1081 else:
1082 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001083 expr.append(indent + '{')
1084 indent = self.incIndent(indent)
1085 # Prefix for value name to display in error message
Mark Lobodzinski6f82eb52016-12-05 07:38:41 -07001086 if value.ispointer == 2:
1087 memberNamePrefix = '{}{}[{}]->'.format(prefix, value.name, indexName)
1088 memberDisplayNamePrefix = ('{}[%i]->'.format(valueDisplayName), indexName)
1089 else:
1090 memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
1091 memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001092 else:
1093 memberNamePrefix = '{}{}->'.format(prefix, value.name)
1094 memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001095 # Expand the struct validation lines
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001096 expr = self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001097 if lenValue:
1098 # Close if and for scopes
1099 indent = self.decIndent(indent)
1100 expr.append(indent + '}\n')
1101 expr.append('}\n')
1102 return expr
1103 #
1104 # Generate the parameter checking code
1105 def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
1106 lines = [] # Generated lines of code
1107 unused = [] # Unused variable names
1108 for value in values:
1109 usedLines = []
1110 lenParam = None
1111 #
1112 # 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.
1113 postProcSpec = {}
1114 postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
1115 postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
1116 postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
1117 #
1118 # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name
1119 valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
1120 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001121 # Check for NULL pointers, ignore the in-out count parameters that
Mark Lobodzinski85672672016-10-13 08:36:42 -06001122 # will be validated with their associated array
1123 if (value.ispointer or value.isstaticarray) and not value.iscount:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001124 # Parameters for function argument generation
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001125 req = 'true' # Parameter cannot be NULL
Mark Lobodzinski85672672016-10-13 08:36:42 -06001126 cpReq = 'true' # Count pointer cannot be NULL
1127 cvReq = 'true' # Count value cannot be 0
1128 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
Mark Lobodzinski85672672016-10-13 08:36:42 -06001129 # Generate required/optional parameter strings for the pointer and count values
1130 if value.isoptional:
1131 req = 'false'
1132 if value.len:
1133 # The parameter is an array with an explicit count parameter
1134 lenParam = self.getLenParam(values, value.len)
1135 lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
1136 if lenParam.ispointer:
1137 # Count parameters that are pointers are inout
1138 if type(lenParam.isoptional) is list:
1139 if lenParam.isoptional[0]:
1140 cpReq = 'false'
1141 if lenParam.isoptional[1]:
1142 cvReq = 'false'
1143 else:
1144 if lenParam.isoptional:
1145 cpReq = 'false'
1146 else:
1147 if lenParam.isoptional:
1148 cvReq = 'false'
1149 #
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001150 # The parameter will not be processed when tagged as 'noautovalidity'
Mark Lobodzinski85672672016-10-13 08:36:42 -06001151 # For the pointer to struct case, the struct pointer will not be validated, but any
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001152 # members not tagged as 'noautovalidity' will be validated
1153 # We special-case the custom allocator checks, as they are explicit but can be auto-generated.
Ricardo Garcia98ba9642019-02-19 11:25:18 +01001154 AllocatorFunctions = ['PFN_vkAllocationFunction', 'PFN_vkReallocationFunction', 'PFN_vkFreeFunction', 'PFN_vkInternalAllocationNotification', 'PFN_vkInternalFreeNotification']
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001155 if value.noautovalidity and value.type not in AllocatorFunctions:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001156 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1157 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1158 else:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001159 if value.type in self.structTypes:
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001160 # If this is a pointer to a struct with an sType field, verify the type
Mark Lobodzinski9cf24dd2017-06-28 14:23:22 -06001161 usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001162 # 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
1163 elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
1164 usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1165 elif value.type in self.flags and value.isconst:
1166 usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1167 elif value.isbool and value.isconst:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001168 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 -06001169 elif value.israngedenum and value.isconst:
Mark Lobodzinskiaff801e2017-07-25 15:29:57 -06001170 enum_value_list = 'All%sEnums' % value.type
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001171 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 -07001172 elif value.name == 'pNext' and value.isconst:
Mark Lobodzinski9ddf9282018-05-31 13:59:59 -06001173 usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001174 else:
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -06001175 usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001176 # 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 -06001177 if value.type in self.validatedStructs:
1178 if value.isconst: # or value.type in self.returnedonly_structs:
1179 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
1180 elif value.type in self.returnedonly_structs:
1181 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001182 # Non-pointer types
1183 else:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001184 # The parameter will not be processes when tagged as 'noautovalidity'
1185 # For the struct case, the struct type will not be validated, but any
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001186 # members not tagged as 'noautovalidity' will be validated
Mark Lobodzinski85672672016-10-13 08:36:42 -06001187 if value.noautovalidity:
1188 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1189 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1190 else:
Mark Lobodzinski024b2822017-06-27 13:22:05 -06001191 vuid_name_tag = structTypeName if structTypeName is not None else funcName
Mark Lobodzinski85672672016-10-13 08:36:42 -06001192 if value.type in self.structTypes:
1193 stype = self.structTypes[value.type]
Dave Houlton413a6782018-05-22 13:01:54 -06001194 vuid = self.GetVuid(value.type, "sType-sType")
Mark Lobodzinskia16ebc72018-06-15 14:47:39 -06001195 undefined_vuid = '"kVUIDUndefined"'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001196 usedLines.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false, kVUIDUndefined, {});\n'.format(
Mike Schuchardt24ac4e72018-08-11 17:37:20 -07001197 funcName, valueDisplayName, valuePrefix, vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001198 elif value.type in self.handleTypes:
1199 if not self.isHandleOptional(value, None):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001200 usedLines.append('skip |= validate_required_handle("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Petr Kraus52758be2019-08-12 00:53:58 +02001201 elif value.type in self.flags and value.type.replace('Flags', 'FlagBits') not in self.flagBits:
1202 vuid = self.GetVuid(vuid_name_tag, "%s-zerobitmask" % (value.name))
1203 usedLines.append('skip |= validate_reserved_flags("{}", {ppp}"{}"{pps}, {pf}{}, {});\n'.format(funcName, valueDisplayName, value.name, vuid, pf=valuePrefix, **postProcSpec))
1204 elif value.type in self.flags or value.type in self.flagBits:
1205 if value.type in self.flags:
1206 flagBitsName = value.type.replace('Flags', 'FlagBits')
1207 flagsType = 'kOptionalFlags' if value.isoptional else 'kRequiredFlags'
1208 invalidVuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
1209 zeroVuid = self.GetVuid(vuid_name_tag, "%s-requiredbitmask" % (value.name))
1210 elif value.type in self.flagBits:
1211 flagBitsName = value.type
1212 flagsType = 'kOptionalSingleBit' if value.isoptional else 'kRequiredSingleBit'
1213 invalidVuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
1214 zeroVuid = invalidVuid
1215 allFlagsName = 'All' + flagBitsName
1216
1217 invalid_vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
1218 allFlagsName = 'All' + flagBitsName
1219 zeroVuidArg = '' if value.isoptional else ', ' + zeroVuid
1220 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 -06001221 elif value.isbool:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001222 usedLines.append('skip |= validate_bool32("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001223 elif value.israngedenum:
Dave Houlton413a6782018-05-22 13:01:54 -06001224 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinski74cb45f2017-07-25 15:10:29 -06001225 enum_value_list = 'All%sEnums' % value.type
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001226 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 -06001227 # If this is a struct, see if it contains members that need to be checked
1228 if value.type in self.validatedStructs:
1229 memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
1230 memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001231 usedLines.append(self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001232 # Append the parameter check to the function body for the current command
1233 if usedLines:
1234 # Apply special conditional checks
1235 if value.condition:
1236 usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
1237 lines += usedLines
1238 elif not value.iscount:
1239 # If no expression was generated for this value, it is unreferenced by the validation function, unless
1240 # it is an array count, which is indirectly referenced for array valiadation.
1241 unused.append(value.name)
Mark Lobodzinskid4950072017-08-01 13:02:20 -06001242 if not lines:
1243 lines.append('// No xml-driven validation\n')
Mark Lobodzinski85672672016-10-13 08:36:42 -06001244 return lines, unused
1245 #
1246 # Generate the struct member check code from the captured data
1247 def processStructMemberData(self):
1248 indent = self.incIndent(None)
1249 for struct in self.structMembers:
1250 #
1251 # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
1252 lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
1253 if lines:
1254 self.validatedStructs[struct.name] = lines
1255 #
1256 # Generate the command param check code from the captured data
1257 def processCmdData(self):
1258 indent = self.incIndent(None)
1259 for command in self.commands:
1260 # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
1261 startIndex = 0 if command.name == 'vkCreateInstance' else 1
1262 lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
Mark Lobodzinski3f10bfe2017-08-23 15:23:23 -06001263 # Cannot validate extension dependencies for device extension APIs having a physical device as their dispatchable object
Mike Schuchardtafd00482017-08-24 15:15:02 -06001264 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 -06001265 ext_test = ''
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001266 if command.params[0].type in ["VkInstance", "VkPhysicalDevice"] or command.name == 'vkCreateInstance':
1267 ext_table_type = 'instance'
1268 else:
1269 ext_table_type = 'device'
Mike Schuchardtafd00482017-08-24 15:15:02 -06001270 for ext in self.required_extensions[command.name]:
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001271 ext_name_define = ''
1272 ext_enable_name = ''
1273 for extension in self.registry.extensions:
1274 if extension.attrib['name'] == ext:
1275 ext_name_define = extension[0][1].get('name')
1276 ext_enable_name = ext_name_define.lower()
1277 ext_enable_name = re.sub('_extension_name', '', ext_enable_name)
1278 break
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001279 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 -06001280 lines.insert(0, ext_test)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001281 if lines:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001282 func_sig = self.getCmdDef(command) + ' {\n'
1283 func_sig = func_sig.split('VKAPI_CALL vk')[1]
1284 cmdDef = 'bool StatelessValidation::PreCallValidate' + func_sig
Mark Lobodzinskid4950072017-08-01 13:02:20 -06001285 cmdDef += '%sbool skip = false;\n' % indent
Mark Lobodzinski70d416b2019-07-08 15:59:43 -06001286 for line in lines:
1287 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 # Insert call to custom-written function if present
1293 if command.name in self.functions_with_manual_checks:
1294 # Generate parameter list for manual fcn and down-chain calls
1295 params_text = ''
1296 for param in command.params:
1297 params_text += '%s, ' % param.name
1298 params_text = params_text[:-2] + ');\n'
Petr Kraus907c3782019-08-12 03:06:46 +02001299 cmdDef += ' if (!skip) skip |= manual_PreCallValidate'+ command.name[2:] + '(' + params_text
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001300 cmdDef += '%sreturn skip;\n' % indent
Mark Lobodzinski85672672016-10-13 08:36:42 -06001301 cmdDef += '}\n'
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001302 self.validation.append(cmdDef)