blob: 173d8bef5bea17b4dc7d48ca177221c98f7d14d8 [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,
67 filename = None,
68 directory = '.',
69 apiname = None,
70 profile = None,
71 versions = '.*',
72 emitversions = '.*',
73 defaultExtensions = None,
74 addExtensions = None,
75 removeExtensions = None,
Mark Lobodzinski62f71562017-10-24 13:41:18 -060076 emitExtensions = None,
Mark Lobodzinski85672672016-10-13 08:36:42 -060077 sortProcedure = regSortFeatures,
78 prefixText = "",
Mark Lobodzinski85672672016-10-13 08:36:42 -060079 apicall = '',
80 apientry = '',
81 apientryp = '',
82 indentFuncProto = True,
83 indentFuncPointer = False,
Mark Lobodzinski62f71562017-10-24 13:41:18 -060084 alignFuncParam = 0,
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -060085 expandEnumerants = True,
86 valid_usage_path = ''):
Mark Lobodzinski85672672016-10-13 08:36:42 -060087 GeneratorOptions.__init__(self, filename, directory, apiname, profile,
88 versions, emitversions, defaultExtensions,
Mark Lobodzinski62f71562017-10-24 13:41:18 -060089 addExtensions, removeExtensions, emitExtensions, sortProcedure)
Mark Lobodzinski85672672016-10-13 08:36:42 -060090 self.prefixText = prefixText
Mark Lobodzinski85672672016-10-13 08:36:42 -060091 self.apicall = apicall
92 self.apientry = apientry
93 self.apientryp = apientryp
94 self.indentFuncProto = indentFuncProto
95 self.indentFuncPointer = indentFuncPointer
96 self.alignFuncParam = alignFuncParam
Mark Lobodzinski62f71562017-10-24 13:41:18 -060097 self.expandEnumerants = expandEnumerants
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -060098 self.valid_usage_path = valid_usage_path
Mark Lobodzinski85672672016-10-13 08:36:42 -060099
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600100# ParameterValidationOutputGenerator - subclass of OutputGenerator.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600101# Generates param checker layer code.
102#
103# ---- methods ----
104# ParamCheckerOutputGenerator(errFile, warnFile, diagFile) - args as for
105# OutputGenerator. Defines additional internal state.
106# ---- methods overriding base class ----
107# beginFile(genOpts)
108# endFile()
109# beginFeature(interface, emit)
110# endFeature()
111# genType(typeinfo,name)
112# genStruct(typeinfo,name)
113# genGroup(groupinfo,name)
114# genEnum(enuminfo, name)
115# genCmd(cmdinfo)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600116class ParameterValidationOutputGenerator(OutputGenerator):
117 """Generate Parameter Validation code based on XML element attributes"""
Mark Lobodzinski85672672016-10-13 08:36:42 -0600118 # This is an ordered list of sections in the header file.
119 ALL_SECTIONS = ['command']
120 def __init__(self,
121 errFile = sys.stderr,
122 warnFile = sys.stderr,
123 diagFile = sys.stdout):
124 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
125 self.INDENT_SPACES = 4
Mark Lobodzinskib6b8bbd2017-02-08 14:37:15 -0700126 self.declarations = []
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700127
128 inline_custom_source_preamble = """
129"""
130
Jeff Bolz7e7e6e02019-01-11 22:53:41 -0600131 # 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 -0700132 # to those functions of the form 'bool manual_PreCallValidateAPIName', where the 'vk' is dropped.
133 # see 'manual_PreCallValidateCreateGraphicsPipelines' as an example.
134 self.functions_with_manual_checks = [
135 'vkCreateInstance',
136 'vkCreateDevice',
137 'vkCreateQueryPool'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700138 'vkCreateRenderPass',
139 'vkCreateRenderPass2KHR',
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700140 'vkCreateBuffer',
141 'vkCreateImage',
142 'vkCreateImageView',
143 'vkCreateGraphicsPipelines',
144 'vkCreateComputePipelines',
145 'vkCreateSampler',
146 'vkCreateDescriptorSetLayout',
147 'vkFreeDescriptorSets',
148 'vkUpdateDescriptorSets',
149 'vkCreateRenderPass',
150 'vkCreateRenderPass2KHR',
151 'vkBeginCommandBuffer',
152 'vkCmdSetViewport',
153 'vkCmdSetScissor',
154 'vkCmdSetLineWidth',
155 'vkCmdDraw',
156 'vkCmdDrawIndirect',
157 'vkCmdDrawIndexedIndirect',
158 'vkCmdCopyImage',
159 'vkCmdBlitImage',
160 'vkCmdCopyBufferToImage',
161 'vkCmdCopyImageToBuffer',
162 'vkCmdUpdateBuffer',
163 'vkCmdFillBuffer',
164 'vkCreateSwapchainKHR',
165 'vkQueuePresentKHR',
166 'vkCreateDescriptorPool',
167 'vkCmdDispatch',
168 'vkCmdDispatchIndirect',
169 'vkCmdDispatchBaseKHR',
170 'vkCmdSetExclusiveScissorNV',
171 'vkCmdSetViewportShadingRatePaletteNV',
172 'vkCmdSetCoarseSampleOrderNV',
173 'vkCmdDrawMeshTasksNV',
174 'vkCmdDrawMeshTasksIndirectNV',
175 'vkCmdDrawMeshTasksIndirectCountNV',
Jeff Bolz7e7e6e02019-01-11 22:53:41 -0600176 'vkAllocateMemory',
Ricardo Garciaa4935972019-02-21 17:43:18 +0100177 'vkCreateAccelerationStructureNV',
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700178 ]
179
Mark Lobodzinski85672672016-10-13 08:36:42 -0600180 # Commands to ignore
181 self.blacklist = [
182 'vkGetInstanceProcAddr',
183 'vkGetDeviceProcAddr',
Mark Young6ba8abe2017-11-09 10:37:04 -0700184 'vkEnumerateInstanceVersion',
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600185 'vkEnumerateInstanceLayerProperties',
186 'vkEnumerateInstanceExtensionProperties',
187 'vkEnumerateDeviceLayerProperties',
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600188 'vkEnumerateDeviceExtensionProperties',
189 ]
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700190
Dustin Gravesce68f082017-03-30 15:42:16 -0600191 # Structure fields to ignore
192 self.structMemberBlacklist = { 'VkWriteDescriptorSet' : ['dstSet'] }
Mark Lobodzinski85672672016-10-13 08:36:42 -0600193 # Validation conditions for some special case struct members that are conditionally validated
194 self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
195 # Header version
196 self.headerVersion = None
197 # Internal state - accumulators for different inner block text
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600198 self.validation = [] # Text comprising the main per-api parameter validation routines
Mark Lobodzinski85672672016-10-13 08:36:42 -0600199 self.stypes = [] # Values from the VkStructureType enumeration
200 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
201 self.handleTypes = set() # Set of handle type names
202 self.commands = [] # List of CommandData records for all Vulkan commands
203 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
204 self.validatedStructs = dict() # Map of structs type names to generated validation code for that struct type
205 self.enumRanges = dict() # Map of enum name to BEGIN/END range values
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600206 self.enumValueLists = '' # String containing enumerated type map definitions
Mark Lobodzinski85672672016-10-13 08:36:42 -0600207 self.flags = set() # Map of flags typenames
208 self.flagBits = dict() # Map of flag bits typename to list of values
Chris Forbes78ea32d2016-11-28 11:14:17 +1300209 self.newFlags = set() # Map of flags typenames /defined in the current feature/
Mike Schuchardtafd00482017-08-24 15:15:02 -0600210 self.required_extensions = dict() # Dictionary of required extensions for each item in the current extension
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600211 self.extension_type = '' # Type of active feature (extension), device or instance
212 self.extension_names = dict() # Dictionary of extension names to extension name defines
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600213 self.structextends_list = [] # List of extensions which extend another struct
214 self.struct_feature_protect = dict() # Dictionary of structnames and FeatureExtraProtect strings
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600215 self.valid_vuids = set() # Set of all valid VUIDs
Dave Houlton413a6782018-05-22 13:01:54 -0600216 self.vuid_dict = dict() # VUID dictionary (from JSON)
217 self.alias_dict = dict() # Dict of cmd|struct aliases
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700218 self.header_file = False # Header file generation flag
219 self.source_file = False # Source file generation flag
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600220 self.returnedonly_structs = []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600221 # Named tuples to store struct and command data
Mark Lobodzinski85672672016-10-13 08:36:42 -0600222 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600223 'isconst', 'isoptional', 'iscount', 'noautovalidity',
224 'len', 'extstructs', 'condition', 'cdecl'])
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600225 self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl', 'extension_type', 'result'])
Mark Lobodzinski85672672016-10-13 08:36:42 -0600226 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600227
Mark Lobodzinski85672672016-10-13 08:36:42 -0600228 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600229 # Generate Copyright comment block for file
230 def GenerateCopyright(self):
231 copyright = '/* *** THIS FILE IS GENERATED - DO NOT EDIT! ***\n'
232 copyright += ' * See parameter_validation_generator.py for modifications\n'
233 copyright += ' *\n'
Dave Houlton413a6782018-05-22 13:01:54 -0600234 copyright += ' * Copyright (c) 2015-2018 The Khronos Group Inc.\n'
235 copyright += ' * Copyright (c) 2015-2018 LunarG, Inc.\n'
236 copyright += ' * Copyright (C) 2015-2018 Google Inc.\n'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600237 copyright += ' *\n'
238 copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
239 copyright += ' * you may not use this file except in compliance with the License.\n'
240 copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n'
241 copyright += ' * You may obtain a copy of the License at\n'
242 copyright += ' *\n'
243 copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n'
244 copyright += ' *\n'
245 copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
246 copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
247 copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
248 copyright += ' * See the License for the specific language governing permissions and\n'
249 copyright += ' * limitations under the License.\n'
250 copyright += ' *\n'
251 copyright += ' * Author: Mark Lobodzinski <mark@LunarG.com>\n'
Dave Houlton413a6782018-05-22 13:01:54 -0600252 copyright += ' * Author: Dave Houlton <daveh@LunarG.com>\n'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600253 copyright += ' */\n\n'
254 return copyright
255 #
256 # Increases the global indent variable
Mark Lobodzinski85672672016-10-13 08:36:42 -0600257 def incIndent(self, indent):
258 inc = ' ' * self.INDENT_SPACES
259 if indent:
260 return indent + inc
261 return inc
262 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600263 # Decreases the global indent variable
Mark Lobodzinski85672672016-10-13 08:36:42 -0600264 def decIndent(self, indent):
265 if indent and (len(indent) > self.INDENT_SPACES):
266 return indent[:-self.INDENT_SPACES]
267 return ''
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600268 #
Dave Houlton413a6782018-05-22 13:01:54 -0600269 # Walk the JSON-derived dict and find all "vuid" key values
270 def ExtractVUIDs(self, d):
271 if hasattr(d, 'items'):
272 for k, v in d.items():
273 if k == "vuid":
274 yield v
275 elif isinstance(v, dict):
276 for s in self.ExtractVUIDs(v):
277 yield s
278 elif isinstance (v, list):
279 for l in v:
280 for s in self.ExtractVUIDs(l):
281 yield s
Mark Lobodzinski85672672016-10-13 08:36:42 -0600282 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600283 # Called at file creation time
Mark Lobodzinski85672672016-10-13 08:36:42 -0600284 def beginFile(self, genOpts):
285 OutputGenerator.beginFile(self, genOpts)
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700286 self.header_file = (genOpts.filename == 'parameter_validation.h')
287 self.source_file = (genOpts.filename == 'parameter_validation.cpp')
288
289 if not self.header_file and not self.source_file:
290 print("Error: Output Filenames have changed, update generator source.\n")
291 sys.exit(1)
292
293 if self.source_file or self.header_file:
294 # Output Copyright text
295 s = self.GenerateCopyright()
296 write(s, file=self.outFile)
297
298 if self.header_file:
299 return
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -0600300
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700301 # Build map of structure type names to VkStructureType enum values
302 # Find all types of category "struct"
303 for struct in self.registry.tree.iterfind('types/type[@category="struct"]'):
304 # Check if struct has member named "sType" of type "VkStructureType" which has values defined
305 stype = struct.find('member[name="sType"][type="VkStructureType"][@values]')
Shahbaz Youssefi23aee922019-01-11 14:04:49 -0500306 if stype is not None:
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700307 # Store VkStructureType value for this type
308 self.structTypes[struct.get('name')] = stype.get('values')
309
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -0600310 self.valid_usage_path = genOpts.valid_usage_path
311 vu_json_filename = os.path.join(self.valid_usage_path + os.sep, 'validusage.json')
312 if os.path.isfile(vu_json_filename):
313 json_file = open(vu_json_filename, 'r')
314 self.vuid_dict = json.load(json_file)
315 json_file.close()
316 if len(self.vuid_dict) == 0:
317 print("Error: Could not find, or error loading %s/validusage.json\n", vu_json_filename)
318 sys.exit(1)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600319 #
Dave Houlton413a6782018-05-22 13:01:54 -0600320 # Build a set of all vuid text strings found in validusage.json
321 for json_vuid_string in self.ExtractVUIDs(self.vuid_dict):
322 self.valid_vuids.add(json_vuid_string)
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600323 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600324 # Headers
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700325 write('#include "chassis.h"', file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600326 self.newline()
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700327 write('#include "stateless_validation.h"', file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600328 self.newline()
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600329 #
330 # Called at end-time for final content output
Mark Lobodzinski85672672016-10-13 08:36:42 -0600331 def endFile(self):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700332 if self.source_file:
333 # C-specific
334 self.newline()
335 write(self.enumValueLists, file=self.outFile)
336 self.newline()
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600337
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700338 pnext_handler = 'bool StatelessValidation::ValidatePnextStructContents(const char *api_name, const ParameterName &parameter_name, const GenericHeader* header) {\n'
339 pnext_handler += ' bool skip = false;\n'
340 pnext_handler += ' switch(header->sType) {\n'
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600341
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700342 # Do some processing here to extract data from validatedstructs...
343 for item in self.structextends_list:
344 postProcSpec = {}
345 postProcSpec['ppp'] = '' if not item else '{postProcPrefix}'
346 postProcSpec['pps'] = '' if not item else '{postProcSuffix}'
347 postProcSpec['ppi'] = '' if not item else '{postProcInsert}'
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600348
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700349 pnext_case = '\n'
350 protect = ''
351 # Guard struct cases with feature ifdefs, if necessary
352 if item in self.struct_feature_protect.keys():
353 protect = self.struct_feature_protect[item]
354 pnext_case += '#ifdef %s\n' % protect
355 pnext_case += ' // Validation code for %s structure members\n' % item
356 pnext_case += ' case %s: {\n' % self.structTypes[item]
357 pnext_case += ' %s *structure = (%s *) header;\n' % (item, item)
358 expr = self.expandStructCode(item, item, 'structure->', '', ' ', [], postProcSpec)
359 struct_validation_source = self.ScrubStructCode(expr)
360 pnext_case += '%s' % struct_validation_source
361 pnext_case += ' } break;\n'
362 if protect is not '':
363 pnext_case += '#endif // %s\n' % protect
364 # Skip functions containing no validation
365 if struct_validation_source != '':
366 pnext_handler += pnext_case;
367 pnext_handler += ' default:\n'
368 pnext_handler += ' skip = false;\n'
369 pnext_handler += ' }\n'
370 pnext_handler += ' return skip;\n'
371 pnext_handler += '}\n'
372 write(pnext_handler, file=self.outFile)
373 self.newline()
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600374
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700375 ext_template = 'bool StatelessValidation::OutputExtensionError(const std::string &api_name, const std::string &extension_name) {\n'
376 ext_template += ' return log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,\n'
377 ext_template += ' kVUID_PVError_ExtensionNotEnabled, "Attemped to call %s() but its required extension %s has not been enabled\\n",\n'
378 ext_template += ' api_name.c_str(), extension_name.c_str());\n'
379 ext_template += '}\n'
380 write(ext_template, file=self.outFile)
381 self.newline()
382 commands_text = '\n'.join(self.validation)
383 write(commands_text, file=self.outFile)
384 self.newline()
385 if self.header_file:
386 # Output declarations and record intercepted procedures
387 write('\n'.join(self.declarations), file=self.outFile)
388 # Finish processing in superclass
389 OutputGenerator.endFile(self)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600390 #
391 # Processing at beginning of each feature or extension
Mark Lobodzinski85672672016-10-13 08:36:42 -0600392 def beginFeature(self, interface, emit):
393 # Start processing in superclass
394 OutputGenerator.beginFeature(self, interface, emit)
395 # C-specific
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600396 # Accumulate includes, defines, types, enums, function pointer typedefs, end function prototypes separately for this
397 # feature. They're only printed in endFeature().
Mark Lobodzinski85672672016-10-13 08:36:42 -0600398 self.headerVersion = None
Mark Lobodzinski85672672016-10-13 08:36:42 -0600399 self.stypes = []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600400 self.commands = []
401 self.structMembers = []
Chris Forbes78ea32d2016-11-28 11:14:17 +1300402 self.newFlags = set()
Mark Lobodzinski62f71562017-10-24 13:41:18 -0600403 self.featureExtraProtect = GetFeatureProtect(interface)
Mike Schuchardtafd00482017-08-24 15:15:02 -0600404 # Get base list of extension dependencies for all items in this extension
405 base_required_extensions = []
Mark Lobodzinski31964ca2017-09-18 14:15:09 -0600406 if "VK_VERSION_1" not in self.featureName:
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600407 # Save Name Define to get correct enable name later
408 nameElem = interface[0][1]
409 name = nameElem.get('name')
410 self.extension_names[self.featureName] = name
411 # This extension is the first dependency for this command
Mike Schuchardtafd00482017-08-24 15:15:02 -0600412 base_required_extensions.append(self.featureName)
413 # Add any defined extension dependencies to the base dependency list for this extension
414 requires = interface.get('requires')
415 if requires is not None:
416 base_required_extensions.extend(requires.split(','))
Mike Schuchardtafd00482017-08-24 15:15:02 -0600417 # Build dictionary of extension dependencies for each item in this extension
418 self.required_extensions = dict()
419 for require_element in interface.findall('require'):
420 # Copy base extension dependency list
421 required_extensions = list(base_required_extensions)
422 # Add any additional extension dependencies specified in this require block
423 additional_extensions = require_element.get('extension')
424 if additional_extensions:
425 required_extensions.extend(additional_extensions.split(','))
426 # Save full extension list for all named items
427 for element in require_element.findall('*[@name]'):
428 self.required_extensions[element.get('name')] = required_extensions
429
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600430 # And note if this is an Instance or Device extension
431 self.extension_type = interface.get('type')
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600432 #
433 # Called at the end of each extension (feature)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600434 def endFeature(self):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700435 if self.header_file:
436 return
Mark Lobodzinski85672672016-10-13 08:36:42 -0600437 # C-specific
438 # Actually write the interface to the output file.
439 if (self.emit):
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600440 # 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 -0600441 # or move it below the 'for section...' loop.
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600442 ifdef = ''
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100443 if (self.featureExtraProtect is not None):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600444 ifdef = '#ifdef %s\n' % self.featureExtraProtect
445 self.validation.append(ifdef)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600446 # Generate the struct member checking code from the captured data
447 self.processStructMemberData()
448 # Generate the command parameter checking code from the captured data
449 self.processCmdData()
450 # Write the declaration for the HeaderVersion
451 if self.headerVersion:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700452 write('const uint32_t GeneratedVulkanHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600453 self.newline()
454 # Write the declarations for the VkFlags values combining all flag bits
Chris Forbes78ea32d2016-11-28 11:14:17 +1300455 for flag in sorted(self.newFlags):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600456 flagBits = flag.replace('Flags', 'FlagBits')
457 if flagBits in self.flagBits:
458 bits = self.flagBits[flagBits]
459 decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
460 for bit in bits[1:]:
461 decl += '|' + bit
462 decl += ';'
463 write(decl, file=self.outFile)
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600464 endif = '\n'
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100465 if (self.featureExtraProtect is not None):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600466 endif = '#endif // %s\n' % self.featureExtraProtect
467 self.validation.append(endif)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600468 # Finish processing in superclass
469 OutputGenerator.endFeature(self)
470 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600471 # Type generation
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700472 def genType(self, typeinfo, name, alias):
Dave Houlton413a6782018-05-22 13:01:54 -0600473 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100474 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600475 self.alias_dict[name]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700476 OutputGenerator.genType(self, typeinfo, name, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600477 typeElem = typeinfo.elem
Mark Lobodzinski87017df2018-05-30 11:29:24 -0600478 # 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 -0600479 category = typeElem.get('category')
480 if (category == 'struct' or category == 'union'):
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700481 self.genStruct(typeinfo, name, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600482 elif (category == 'handle'):
483 self.handleTypes.add(name)
484 elif (category == 'bitmask'):
485 self.flags.add(name)
Chris Forbes78ea32d2016-11-28 11:14:17 +1300486 self.newFlags.add(name)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600487 elif (category == 'define'):
488 if name == 'VK_HEADER_VERSION':
489 nameElem = typeElem.find('name')
490 self.headerVersion = noneStr(nameElem.tail).strip()
491 #
492 # Struct parameter check generation.
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600493 # This is a special case of the <type> tag where the contents are interpreted as a set of <member> tags instead of freeform C
494 # type declarations. The <member> tags are just like <param> tags - they are a declaration of a struct or union member.
495 # Only simple member declarations are supported (no nested structs etc.)
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700496 def genStruct(self, typeinfo, typeName, alias):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700497 if not self.source_file:
498 return
Dave Houlton413a6782018-05-22 13:01:54 -0600499 # alias has already been recorded in genType, above
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700500 OutputGenerator.genStruct(self, typeinfo, typeName, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600501 conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
502 members = typeinfo.elem.findall('.//member')
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600503 if self.featureExtraProtect is not None:
504 self.struct_feature_protect[typeName] = self.featureExtraProtect
Mark Lobodzinski85672672016-10-13 08:36:42 -0600505 #
506 # Iterate over members once to get length parameters for arrays
507 lens = set()
508 for member in members:
509 len = self.getLen(member)
510 if len:
511 lens.add(len)
512 #
513 # Generate member info
514 membersInfo = []
515 for member in members:
516 # Get the member's type and name
517 info = self.getTypeNameTuple(member)
518 type = info[0]
519 name = info[1]
520 stypeValue = ''
521 cdecl = self.makeCParamDecl(member, 0)
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700522
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600523 # Store pointer/array/string info -- Check for parameter name in lens set
Mark Lobodzinski85672672016-10-13 08:36:42 -0600524 iscount = False
525 if name in lens:
526 iscount = True
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600527 # The pNext members are not tagged as optional, but are treated as optional for parameter NULL checks. Static array
528 # members are also treated as optional to skip NULL pointer validation, as they won't be NULL.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600529 isstaticarray = self.paramIsStaticArray(member)
530 isoptional = False
531 if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
532 isoptional = True
Dustin Gravesce68f082017-03-30 15:42:16 -0600533 # Determine if value should be ignored by code generation.
534 noautovalidity = False
535 if (member.attrib.get('noautovalidity') is not None) or ((typeName in self.structMemberBlacklist) and (name in self.structMemberBlacklist[typeName])):
536 noautovalidity = True
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600537 structextends = False
Mark Lobodzinski85672672016-10-13 08:36:42 -0600538 membersInfo.append(self.CommandParam(type=type, name=name,
539 ispointer=self.paramIsPointer(member),
540 isstaticarray=isstaticarray,
541 isbool=True if type == 'VkBool32' else False,
542 israngedenum=True if type in self.enumRanges else False,
543 isconst=True if 'const' in cdecl else False,
544 isoptional=isoptional,
545 iscount=iscount,
Dustin Gravesce68f082017-03-30 15:42:16 -0600546 noautovalidity=noautovalidity,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600547 len=self.getLen(member),
Mike Schuchardta40d0b02017-07-23 12:47:47 -0600548 extstructs=self.registry.validextensionstructs[typeName] if name == 'pNext' else None,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600549 condition=conditions[name] if conditions and name in conditions else None,
550 cdecl=cdecl))
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600551 # If this struct extends another, keep its name in list for further processing
552 if typeinfo.elem.attrib.get('structextends') is not None:
553 self.structextends_list.append(typeName)
554 # Returnedonly structs should have most of their members ignored -- on entry, we only care about validating the sType and
555 # pNext members. Everything else will be overwritten by the callee.
556 if typeinfo.elem.attrib.get('returnedonly') is not None:
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600557 self.returnedonly_structs.append(typeName)
558 membersInfo = [m for m in membersInfo if m.name in ('sType', 'pNext')]
Mark Lobodzinski85672672016-10-13 08:36:42 -0600559 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
560 #
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600561 # Capture group (e.g. C "enum" type) info to be used for param check code generation.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600562 # These are concatenated together with other types.
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700563 def genGroup(self, groupinfo, groupName, alias):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700564 if not self.source_file:
565 return
Dave Houlton413a6782018-05-22 13:01:54 -0600566 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100567 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600568 self.alias_dict[groupName]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700569 OutputGenerator.genGroup(self, groupinfo, groupName, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600570 groupElem = groupinfo.elem
Mark Lobodzinski85672672016-10-13 08:36:42 -0600571 # Store the sType values
572 if groupName == 'VkStructureType':
573 for elem in groupElem.findall('enum'):
574 self.stypes.append(elem.get('name'))
575 elif 'FlagBits' in groupName:
576 bits = []
577 for elem in groupElem.findall('enum'):
Shannon McPherson533a66c2018-08-21 12:09:25 -0600578 if elem.get('supported') != 'disabled':
579 bits.append(elem.get('name'))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600580 if bits:
581 self.flagBits[groupName] = bits
582 else:
583 # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
584 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
585 expandPrefix = expandName
586 expandSuffix = ''
587 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
588 if expandSuffixMatch:
589 expandSuffix = '_' + expandSuffixMatch.group()
590 # Strip off the suffix from the prefix
591 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
592 isEnum = ('FLAG_BITS' not in expandPrefix)
593 if isEnum:
594 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600595 # Create definition for a list containing valid enum values for this enumerated type
596 enum_entry = 'const std::vector<%s> All%sEnums = {' % (groupName, groupName)
597 for enum in groupElem:
598 name = enum.get('name')
Mark Lobodzinski117d88f2017-07-27 12:09:08 -0600599 if name is not None and enum.get('supported') != 'disabled':
600 enum_entry += '%s, ' % name
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600601 enum_entry += '};\n'
602 self.enumValueLists += enum_entry
Mark Lobodzinski85672672016-10-13 08:36:42 -0600603 #
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600604 # Capture command parameter info to be used for param check code generation.
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700605 def genCmd(self, cmdinfo, name, alias):
Dave Houlton413a6782018-05-22 13:01:54 -0600606 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100607 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600608 self.alias_dict[name]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700609 OutputGenerator.genCmd(self, cmdinfo, name, alias)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600610 decls = self.makeCDecls(cmdinfo.elem)
611 typedef = decls[1]
612 typedef = typedef.split(')',1)[1]
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700613 if self.header_file:
614 if name not in self.blacklist:
615 if (self.featureExtraProtect is not None):
616 self.declarations += [ '#ifdef %s' % self.featureExtraProtect ]
617 # Strip off 'vk' from API name
618 self.declarations += [ '%s%s' % ('bool PreCallValidate', decls[0].split("VKAPI_CALL vk")[1])]
619 if (self.featureExtraProtect is not None):
620 self.declarations += [ '#endif' ]
621 if self.source_file:
622 if name not in self.blacklist:
623 params = cmdinfo.elem.findall('param')
624 # Get list of array lengths
625 lens = set()
626 for param in params:
627 len = self.getLen(param)
628 if len:
629 lens.add(len)
630 # Get param info
631 paramsInfo = []
632 for param in params:
633 paramInfo = self.getTypeNameTuple(param)
634 cdecl = self.makeCParamDecl(param, 0)
635 # Check for parameter name in lens set
636 iscount = False
637 if paramInfo[1] in lens:
638 iscount = True
639 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
640 ispointer=self.paramIsPointer(param),
641 isstaticarray=self.paramIsStaticArray(param),
642 isbool=True if paramInfo[0] == 'VkBool32' else False,
643 israngedenum=True if paramInfo[0] in self.enumRanges else False,
644 isconst=True if 'const' in cdecl else False,
645 isoptional=self.paramIsOptional(param),
646 iscount=iscount,
647 noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
648 len=self.getLen(param),
649 extstructs=None,
650 condition=None,
651 cdecl=cdecl))
652 # Save return value information, if any
653 result_type = ''
654 resultinfo = cmdinfo.elem.find('proto/type')
655 if (resultinfo is not None and resultinfo.text != 'void'):
656 result_type = resultinfo.text
657 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 -0600658 #
659 # Check if the parameter passed in is a pointer
660 def paramIsPointer(self, param):
661 ispointer = 0
662 paramtype = param.find('type')
663 if (paramtype.tail is not None) and ('*' in paramtype.tail):
664 ispointer = paramtype.tail.count('*')
665 elif paramtype.text[:4] == 'PFN_':
666 # Treat function pointer typedefs as a pointer to a single value
667 ispointer = 1
668 return ispointer
669 #
670 # Check if the parameter passed in is a static array
671 def paramIsStaticArray(self, param):
672 isstaticarray = 0
673 paramname = param.find('name')
674 if (paramname.tail is not None) and ('[' in paramname.tail):
675 isstaticarray = paramname.tail.count('[')
676 return isstaticarray
677 #
678 # Check if the parameter passed in is optional
679 # Returns a list of Boolean values for comma separated len attributes (len='false,true')
680 def paramIsOptional(self, param):
681 # See if the handle is optional
682 isoptional = False
683 # Simple, if it's optional, return true
684 optString = param.attrib.get('optional')
685 if optString:
686 if optString == 'true':
687 isoptional = True
688 elif ',' in optString:
689 opts = []
690 for opt in optString.split(','):
691 val = opt.strip()
692 if val == 'true':
693 opts.append(True)
694 elif val == 'false':
695 opts.append(False)
696 else:
697 print('Unrecognized len attribute value',val)
698 isoptional = opts
699 return isoptional
700 #
701 # Check if the handle passed in is optional
702 # Uses the same logic as ValidityOutputGenerator.isHandleOptional
703 def isHandleOptional(self, param, lenParam):
704 # Simple, if it's optional, return true
705 if param.isoptional:
706 return True
707 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
708 if param.noautovalidity:
709 return True
710 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
711 if lenParam and lenParam.isoptional:
712 return True
713 return False
714 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600715 # Retrieve the value of the len tag
716 def getLen(self, param):
717 result = None
718 len = param.attrib.get('len')
719 if len and len != 'null-terminated':
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600720 # For string arrays, 'len' can look like 'count,null-terminated', indicating that we have a null terminated array of
721 # 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 -0600722 if 'null-terminated' in len:
723 result = len.split(',')[0]
724 else:
725 result = len
726 result = str(result).replace('::', '->')
727 return result
728 #
729 # Retrieve the type and name for a parameter
730 def getTypeNameTuple(self, param):
731 type = ''
732 name = ''
733 for elem in param:
734 if elem.tag == 'type':
735 type = noneStr(elem.text)
736 elif elem.tag == 'name':
737 name = noneStr(elem.text)
738 return (type, name)
739 #
740 # Find a named parameter in a parameter list
741 def getParamByName(self, params, name):
742 for param in params:
743 if param.name == name:
744 return param
745 return None
746 #
747 # Extract length values from latexmath. Currently an inflexible solution that looks for specific
748 # patterns that are found in vk.xml. Will need to be updated when new patterns are introduced.
749 def parseLateXMath(self, source):
750 name = 'ERROR'
751 decoratedName = 'ERROR'
752 if 'mathit' in source:
Mark Lobodzinski36c33862017-02-13 10:15:53 -0700753 # Matches expressions similar to 'latexmath:[\lceil{\mathit{rasterizationSamples} \over 32}\rceil]'
754 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 -0600755 if not match or match.group(1) != match.group(4):
756 raise 'Unrecognized latexmath expression'
757 name = match.group(2)
758 decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
759 else:
Mark Lobodzinski36c33862017-02-13 10:15:53 -0700760 # Matches expressions similar to 'latexmath : [dataSize \over 4]'
Shannon McPhersonbd68df02018-10-29 15:04:41 -0600761 match = re.match(r'latexmath\s*\:\s*\[\s*(\\textrm\{)?(\w+)\}?\s*\\over\s*(\d+)\s*\]', source)
762 name = match.group(2)
763 decoratedName = '{}/{}'.format(*match.group(2, 3))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600764 return name, decoratedName
765 #
766 # Get the length paramater record for the specified parameter name
767 def getLenParam(self, params, name):
768 lenParam = None
769 if name:
770 if '->' in name:
771 # The count is obtained by dereferencing a member of a struct parameter
772 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600773 isstaticarray=None, isoptional=False, type=None, noautovalidity=False,
774 len=None, extstructs=None, condition=None, cdecl=None)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600775 elif 'latexmath' in name:
776 lenName, decoratedName = self.parseLateXMath(name)
777 lenParam = self.getParamByName(params, lenName)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600778 else:
779 lenParam = self.getParamByName(params, name)
780 return lenParam
781 #
782 # Convert a vulkan.h command declaration into a parameter_validation.h definition
783 def getCmdDef(self, cmd):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600784 # Strip the trailing ';' and split into individual lines
785 lines = cmd.cdecl[:-1].split('\n')
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600786 cmd_hdr = '\n'.join(lines)
787 return cmd_hdr
Mark Lobodzinski85672672016-10-13 08:36:42 -0600788 #
789 # Generate the code to check for a NULL dereference before calling the
790 # validation function
791 def genCheckedLengthCall(self, name, exprs):
792 count = name.count('->')
793 if count:
794 checkedExpr = []
795 localIndent = ''
796 elements = name.split('->')
797 # Open the if expression blocks
798 for i in range(0, count):
799 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
800 localIndent = self.incIndent(localIndent)
801 # Add the validation expression
802 for expr in exprs:
803 checkedExpr.append(localIndent + expr)
804 # Close the if blocks
805 for i in range(0, count):
806 localIndent = self.decIndent(localIndent)
807 checkedExpr.append(localIndent + '}\n')
808 return [checkedExpr]
809 # No if statements were required
810 return exprs
811 #
812 # Generate code to check for a specific condition before executing validation code
813 def genConditionalCall(self, prefix, condition, exprs):
814 checkedExpr = []
815 localIndent = ''
816 formattedCondition = condition.format(prefix)
817 checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
818 checkedExpr.append(localIndent + '{\n')
819 localIndent = self.incIndent(localIndent)
820 for expr in exprs:
821 checkedExpr.append(localIndent + expr)
822 localIndent = self.decIndent(localIndent)
823 checkedExpr.append(localIndent + '}\n')
824 return [checkedExpr]
825 #
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600826 # Get VUID identifier from implicit VUID tag
Dave Houlton413a6782018-05-22 13:01:54 -0600827 def GetVuid(self, name, suffix):
828 vuid_string = 'VUID-%s-%s' % (name, suffix)
829 vuid = "kVUIDUndefined"
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600830 if '->' in vuid_string:
Dave Houlton413a6782018-05-22 13:01:54 -0600831 return vuid
832 if vuid_string in self.valid_vuids:
833 vuid = "\"%s\"" % vuid_string
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600834 else:
Dave Houlton413a6782018-05-22 13:01:54 -0600835 if name in self.alias_dict:
836 alias_string = 'VUID-%s-%s' % (self.alias_dict[name], suffix)
837 if alias_string in self.valid_vuids:
Shannon McPherson23d97212019-02-18 13:39:42 -0700838 vuid = "\"%s\"" % alias_string
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600839 return vuid
840 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600841 # Generate the sType check string
Mark Lobodzinski9cf24dd2017-06-28 14:23:22 -0600842 def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600843 checkExpr = []
844 stype = self.structTypes[value.type]
Mark Lobodzinski59603552018-05-29 16:14:59 -0600845 vuid_name = struct_type_name if struct_type_name is not None else funcPrintName
846 stype_vuid = self.GetVuid(value.type, "sType-sType")
847 param_vuid = self.GetVuid(vuid_name, "%s-parameter" % value.name)
848
Mark Lobodzinski85672672016-10-13 08:36:42 -0600849 if lenValue:
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800850 count_required_vuid = self.GetVuid(vuid_name, "%s-arraylength" % lenValue.name)
851
Mark Lobodzinski85672672016-10-13 08:36:42 -0600852 # This is an array with a pointer to a count value
853 if lenValue.ispointer:
854 # 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 -0800855 checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {}, {});\n'.format(
856 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 -0600857 # This is an array with an integer count value
858 else:
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800859 checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {});\n'.format(
860 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 -0600861 # This is an individual struct
862 else:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700863 checkExpr.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {}, {}, {});\n'.format(
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700864 funcPrintName, valuePrintName, prefix, valueRequired, param_vuid, stype_vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600865 return checkExpr
866 #
867 # Generate the handle check string
868 def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
869 checkExpr = []
870 if lenValue:
871 if lenValue.ispointer:
872 # This is assumed to be an output array with a pointer to a count value
873 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
874 else:
875 # This is an array with an integer count value
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700876 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 -0600877 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
878 else:
879 # This is assumed to be an output handle pointer
880 raise('Unsupported parameter validation case: Output handles are not NULL checked')
881 return checkExpr
882 #
883 # Generate check string for an array of VkFlags values
884 def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
885 checkExpr = []
886 flagBitsName = value.type.replace('Flags', 'FlagBits')
887 if not flagBitsName in self.flagBits:
888 raise('Unsupported parameter validation case: array of reserved VkFlags')
889 else:
890 allFlags = 'All' + flagBitsName
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700891 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 -0600892 return checkExpr
893 #
894 # Generate pNext check string
Mark Lobodzinski3c828522017-06-26 13:05:57 -0600895 def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600896 checkExpr = []
897 # Generate an array of acceptable VkStructureType values for pNext
898 extStructCount = 0
899 extStructVar = 'NULL'
900 extStructNames = 'NULL'
Dave Houlton413a6782018-05-22 13:01:54 -0600901 vuid = self.GetVuid(struct_type_name, "pNext-pNext")
Mark Lobodzinski85672672016-10-13 08:36:42 -0600902 if value.extstructs:
Mike Schuchardtc73d07e2017-07-12 10:10:01 -0600903 extStructVar = 'allowed_structs_{}'.format(struct_type_name)
904 extStructCount = 'ARRAY_SIZE({})'.format(extStructVar)
Mike Schuchardta40d0b02017-07-23 12:47:47 -0600905 extStructNames = '"' + ', '.join(value.extstructs) + '"'
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700906 checkExpr.append('const VkStructureType {}[] = {{ {} }};\n'.format(extStructVar, ', '.join([self.structTypes[s] for s in value.extstructs])))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700907 checkExpr.append('skip |= validate_struct_pnext("{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedVulkanHeaderVersion, {});\n'.format(
Mark Lobodzinski3c828522017-06-26 13:05:57 -0600908 funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, vuid, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600909 return checkExpr
910 #
911 # Generate the pointer check string
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600912 def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600913 checkExpr = []
Mark Lobodzinskidead0b62017-06-28 13:22:03 -0600914 vuid_tag_name = struct_type_name if struct_type_name is not None else funcPrintName
Mark Lobodzinski85672672016-10-13 08:36:42 -0600915 if lenValue:
Dave Houlton413a6782018-05-22 13:01:54 -0600916 count_required_vuid = self.GetVuid(vuid_tag_name, "%s-arraylength" % (lenValue.name))
917 array_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600918 # This is an array with a pointer to a count value
919 if lenValue.ispointer:
920 # If count and array parameters are optional, there will be no validation
921 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
922 # 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 -0700923 checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600924 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 -0600925 # This is an array with an integer count value
926 else:
927 # If count and array parameters are optional, there will be no validation
928 if valueRequired == 'true' or lenValueRequired == 'true':
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600929 if value.type != 'char':
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700930 checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600931 funcPrintName, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
932 else:
933 # Arrays of strings receive special processing
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700934 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 -0600935 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 -0600936 if checkExpr:
937 if lenValue and ('->' in lenValue.name):
938 # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
939 checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
940 # This is an individual struct that is not allowed to be NULL
941 elif not value.isoptional:
942 # Function pointers need a reinterpret_cast to void*
Dave Houlton413a6782018-05-22 13:01:54 -0600943 ptr_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600944 if value.type[:4] == 'PFN_':
Dave Houlton413a6782018-05-22 13:01:54 -0600945 allocator_dict = {'pfnAllocation': '"VUID-VkAllocationCallbacks-pfnAllocation-00632"',
946 'pfnReallocation': '"VUID-VkAllocationCallbacks-pfnReallocation-00633"',
947 'pfnFree': '"VUID-VkAllocationCallbacks-pfnFree-00634"',
948 'pfnInternalAllocation': '"VUID-VkAllocationCallbacks-pfnInternalAllocation-00635"'
Mark Lobodzinski02fa1972017-06-28 14:46:14 -0600949 }
950 vuid = allocator_dict.get(value.name)
951 if vuid is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600952 ptr_required_vuid = vuid
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700953 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 -0600954 else:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700955 checkExpr.append('skip |= validate_required_pointer("{}", {ppp}"{}"{pps}, {}{}, {});\n'.format(funcPrintName, valuePrintName, prefix, value.name, ptr_required_vuid, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600956 return checkExpr
957 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -0600958 # Process struct member validation code, performing name substitution if required
Mark Lobodzinski85672672016-10-13 08:36:42 -0600959 def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
960 # Build format specifier list
961 kwargs = {}
962 if '{postProcPrefix}' in line:
963 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
964 if type(memberDisplayNamePrefix) is tuple:
965 kwargs['postProcPrefix'] = 'ParameterName('
966 else:
967 kwargs['postProcPrefix'] = postProcSpec['ppp']
968 if '{postProcSuffix}' in line:
969 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
970 if type(memberDisplayNamePrefix) is tuple:
971 kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
972 else:
973 kwargs['postProcSuffix'] = postProcSpec['pps']
974 if '{postProcInsert}' in line:
975 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
976 if type(memberDisplayNamePrefix) is tuple:
977 kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
978 else:
979 kwargs['postProcInsert'] = postProcSpec['ppi']
980 if '{funcName}' in line:
981 kwargs['funcName'] = funcName
982 if '{valuePrefix}' in line:
983 kwargs['valuePrefix'] = memberNamePrefix
984 if '{displayNamePrefix}' in line:
985 # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
986 if type(memberDisplayNamePrefix) is tuple:
987 kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
988 else:
989 kwargs['displayNamePrefix'] = memberDisplayNamePrefix
990
991 if kwargs:
992 # Need to escape the C++ curly braces
993 if 'IndexVector' in line:
994 line = line.replace('IndexVector{ ', 'IndexVector{{ ')
995 line = line.replace(' }),', ' }}),')
996 return line.format(**kwargs)
997 return line
998 #
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600999 # Process struct member validation code, stripping metadata
1000 def ScrubStructCode(self, code):
1001 scrubbed_lines = ''
1002 for line in code:
1003 if 'validate_struct_pnext' in line:
1004 continue
1005 if 'allowed_structs' in line:
1006 continue
1007 if 'xml-driven validation' in line:
1008 continue
1009 line = line.replace('{postProcPrefix}', '')
1010 line = line.replace('{postProcSuffix}', '')
1011 line = line.replace('{postProcInsert}', '')
1012 line = line.replace('{funcName}', '')
1013 line = line.replace('{valuePrefix}', '')
1014 line = line.replace('{displayNamePrefix}', '')
1015 line = line.replace('{IndexVector}', '')
1016 line = line.replace('local_data->', '')
1017 scrubbed_lines += line
1018 return scrubbed_lines
1019 #
Mark Lobodzinski85672672016-10-13 08:36:42 -06001020 # Process struct validation code for inclusion in function or parent struct validation code
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001021 def expandStructCode(self, item_type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
1022 lines = self.validatedStructs[item_type]
Mark Lobodzinski85672672016-10-13 08:36:42 -06001023 for line in lines:
1024 if output:
1025 output[-1] += '\n'
1026 if type(line) is list:
1027 for sub in line:
1028 output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1029 else:
1030 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1031 return output
1032 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001033 # Process struct pointer/array validation code, performing name substitution if required
Mark Lobodzinski85672672016-10-13 08:36:42 -06001034 def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
1035 expr = []
1036 expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
1037 expr.append('{')
1038 indent = self.incIndent(None)
1039 if lenValue:
1040 # Need to process all elements in the array
1041 indexName = lenValue.name.replace('Count', 'Index')
1042 expr[-1] += '\n'
Mark Young39389872017-01-19 21:10:49 -07001043 if lenValue.ispointer:
1044 # If the length value is a pointer, de-reference it for the count.
1045 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < *{}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
1046 else:
1047 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001048 expr.append(indent + '{')
1049 indent = self.incIndent(indent)
1050 # Prefix for value name to display in error message
Mark Lobodzinski6f82eb52016-12-05 07:38:41 -07001051 if value.ispointer == 2:
1052 memberNamePrefix = '{}{}[{}]->'.format(prefix, value.name, indexName)
1053 memberDisplayNamePrefix = ('{}[%i]->'.format(valueDisplayName), indexName)
1054 else:
1055 memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
1056 memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001057 else:
1058 memberNamePrefix = '{}{}->'.format(prefix, value.name)
1059 memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001060 # Expand the struct validation lines
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001061 expr = self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001062 if lenValue:
1063 # Close if and for scopes
1064 indent = self.decIndent(indent)
1065 expr.append(indent + '}\n')
1066 expr.append('}\n')
1067 return expr
1068 #
1069 # Generate the parameter checking code
1070 def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
1071 lines = [] # Generated lines of code
1072 unused = [] # Unused variable names
1073 for value in values:
1074 usedLines = []
1075 lenParam = None
1076 #
1077 # 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.
1078 postProcSpec = {}
1079 postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
1080 postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
1081 postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
1082 #
1083 # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name
1084 valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
1085 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001086 # Check for NULL pointers, ignore the in-out count parameters that
Mark Lobodzinski85672672016-10-13 08:36:42 -06001087 # will be validated with their associated array
1088 if (value.ispointer or value.isstaticarray) and not value.iscount:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001089 # Parameters for function argument generation
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001090 req = 'true' # Parameter cannot be NULL
Mark Lobodzinski85672672016-10-13 08:36:42 -06001091 cpReq = 'true' # Count pointer cannot be NULL
1092 cvReq = 'true' # Count value cannot be 0
1093 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
Mark Lobodzinski85672672016-10-13 08:36:42 -06001094 # Generate required/optional parameter strings for the pointer and count values
1095 if value.isoptional:
1096 req = 'false'
1097 if value.len:
1098 # The parameter is an array with an explicit count parameter
1099 lenParam = self.getLenParam(values, value.len)
1100 lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
1101 if lenParam.ispointer:
1102 # Count parameters that are pointers are inout
1103 if type(lenParam.isoptional) is list:
1104 if lenParam.isoptional[0]:
1105 cpReq = 'false'
1106 if lenParam.isoptional[1]:
1107 cvReq = 'false'
1108 else:
1109 if lenParam.isoptional:
1110 cpReq = 'false'
1111 else:
1112 if lenParam.isoptional:
1113 cvReq = 'false'
1114 #
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001115 # The parameter will not be processed when tagged as 'noautovalidity'
Mark Lobodzinski85672672016-10-13 08:36:42 -06001116 # For the pointer to struct case, the struct pointer will not be validated, but any
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001117 # members not tagged as 'noautovalidity' will be validated
1118 # We special-case the custom allocator checks, as they are explicit but can be auto-generated.
1119 AllocatorFunctions = ['PFN_vkAllocationFunction', 'PFN_vkReallocationFunction', 'PFN_vkFreeFunction']
1120 if value.noautovalidity and value.type not in AllocatorFunctions:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001121 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1122 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1123 else:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001124 if value.type in self.structTypes:
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001125 # If this is a pointer to a struct with an sType field, verify the type
Mark Lobodzinski9cf24dd2017-06-28 14:23:22 -06001126 usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001127 # 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
1128 elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
1129 usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1130 elif value.type in self.flags and value.isconst:
1131 usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1132 elif value.isbool and value.isconst:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001133 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 -06001134 elif value.israngedenum and value.isconst:
Mark Lobodzinskiaff801e2017-07-25 15:29:57 -06001135 enum_value_list = 'All%sEnums' % value.type
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001136 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 -07001137 elif value.name == 'pNext' and value.isconst:
Mark Lobodzinski9ddf9282018-05-31 13:59:59 -06001138 usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001139 else:
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -06001140 usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001141 # 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 -06001142 if value.type in self.validatedStructs:
1143 if value.isconst: # or value.type in self.returnedonly_structs:
1144 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
1145 elif value.type in self.returnedonly_structs:
1146 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001147 # Non-pointer types
1148 else:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001149 # The parameter will not be processes when tagged as 'noautovalidity'
1150 # For the struct case, the struct type will not be validated, but any
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001151 # members not tagged as 'noautovalidity' will be validated
Mark Lobodzinski85672672016-10-13 08:36:42 -06001152 if value.noautovalidity:
1153 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1154 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1155 else:
Mark Lobodzinski024b2822017-06-27 13:22:05 -06001156 vuid_name_tag = structTypeName if structTypeName is not None else funcName
Mark Lobodzinski85672672016-10-13 08:36:42 -06001157 if value.type in self.structTypes:
1158 stype = self.structTypes[value.type]
Dave Houlton413a6782018-05-22 13:01:54 -06001159 vuid = self.GetVuid(value.type, "sType-sType")
Mark Lobodzinskia16ebc72018-06-15 14:47:39 -06001160 undefined_vuid = '"kVUIDUndefined"'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001161 usedLines.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false, kVUIDUndefined, {});\n'.format(
Mike Schuchardt24ac4e72018-08-11 17:37:20 -07001162 funcName, valueDisplayName, valuePrefix, vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001163 elif value.type in self.handleTypes:
1164 if not self.isHandleOptional(value, None):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001165 usedLines.append('skip |= validate_required_handle("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001166 elif value.type in self.flags:
1167 flagBitsName = value.type.replace('Flags', 'FlagBits')
1168 if not flagBitsName in self.flagBits:
Dave Houlton413a6782018-05-22 13:01:54 -06001169 vuid = self.GetVuid(vuid_name_tag, "%s-zerobitmask" % (value.name))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001170 usedLines.append('skip |= validate_reserved_flags("{}", {ppp}"{}"{pps}, {pf}{}, {});\n'.format(funcName, valueDisplayName, value.name, vuid, pf=valuePrefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001171 else:
Mark Lobodzinskie643fcc2017-06-26 16:27:15 -06001172 if value.isoptional:
1173 flagsRequired = 'false'
Dave Houlton413a6782018-05-22 13:01:54 -06001174 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinskie643fcc2017-06-26 16:27:15 -06001175 else:
1176 flagsRequired = 'true'
Dave Houlton413a6782018-05-22 13:01:54 -06001177 vuid = self.GetVuid(vuid_name_tag, "%s-requiredbitmask" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001178 allFlagsName = 'All' + flagBitsName
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001179 usedLines.append('skip |= validate_flags("{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {}, false, {});\n'.format(funcName, valueDisplayName, flagBitsName, allFlagsName, value.name, flagsRequired, vuid, pf=valuePrefix, **postProcSpec))
Mike Schuchardt47619c82017-05-31 09:14:22 -06001180 elif value.type in self.flagBits:
1181 flagsRequired = 'false' if value.isoptional else 'true'
1182 allFlagsName = 'All' + value.type
Dave Houlton413a6782018-05-22 13:01:54 -06001183 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001184 usedLines.append('skip |= validate_flags("{}", {ppp}"{}"{pps}, "{}", {}, {pf}{}, {}, true, {});\n'.format(funcName, valueDisplayName, value.type, allFlagsName, value.name, flagsRequired, vuid, pf=valuePrefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001185 elif value.isbool:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001186 usedLines.append('skip |= validate_bool32("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001187 elif value.israngedenum:
Dave Houlton413a6782018-05-22 13:01:54 -06001188 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinski74cb45f2017-07-25 15:10:29 -06001189 enum_value_list = 'All%sEnums' % value.type
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001190 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 -06001191 # If this is a struct, see if it contains members that need to be checked
1192 if value.type in self.validatedStructs:
1193 memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
1194 memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001195 usedLines.append(self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001196 # Append the parameter check to the function body for the current command
1197 if usedLines:
1198 # Apply special conditional checks
1199 if value.condition:
1200 usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
1201 lines += usedLines
1202 elif not value.iscount:
1203 # If no expression was generated for this value, it is unreferenced by the validation function, unless
1204 # it is an array count, which is indirectly referenced for array valiadation.
1205 unused.append(value.name)
Mark Lobodzinskid4950072017-08-01 13:02:20 -06001206 if not lines:
1207 lines.append('// No xml-driven validation\n')
Mark Lobodzinski85672672016-10-13 08:36:42 -06001208 return lines, unused
1209 #
1210 # Generate the struct member check code from the captured data
1211 def processStructMemberData(self):
1212 indent = self.incIndent(None)
1213 for struct in self.structMembers:
1214 #
1215 # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
1216 lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
1217 if lines:
1218 self.validatedStructs[struct.name] = lines
1219 #
1220 # Generate the command param check code from the captured data
1221 def processCmdData(self):
1222 indent = self.incIndent(None)
1223 for command in self.commands:
1224 # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
1225 startIndex = 0 if command.name == 'vkCreateInstance' else 1
1226 lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
Mark Lobodzinski3f10bfe2017-08-23 15:23:23 -06001227 # Cannot validate extension dependencies for device extension APIs having a physical device as their dispatchable object
Mike Schuchardtafd00482017-08-24 15:15:02 -06001228 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 -06001229 ext_test = ''
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001230 if command.params[0].type in ["VkInstance", "VkPhysicalDevice"] or command.name == 'vkCreateInstance':
1231 ext_table_type = 'instance'
1232 else:
1233 ext_table_type = 'device'
Mike Schuchardtafd00482017-08-24 15:15:02 -06001234 for ext in self.required_extensions[command.name]:
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001235 ext_name_define = ''
1236 ext_enable_name = ''
1237 for extension in self.registry.extensions:
1238 if extension.attrib['name'] == ext:
1239 ext_name_define = extension[0][1].get('name')
1240 ext_enable_name = ext_name_define.lower()
1241 ext_enable_name = re.sub('_extension_name', '', ext_enable_name)
1242 break
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001243 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 -06001244 lines.insert(0, ext_test)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001245 if lines:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001246 func_sig = self.getCmdDef(command) + ' {\n'
1247 func_sig = func_sig.split('VKAPI_CALL vk')[1]
1248 cmdDef = 'bool StatelessValidation::PreCallValidate' + func_sig
Mark Lobodzinskid4950072017-08-01 13:02:20 -06001249 cmdDef += '%sbool skip = false;\n' % indent
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001250 # Insert call to custom-written function if present
1251 if command.name in self.functions_with_manual_checks:
1252 # Generate parameter list for manual fcn and down-chain calls
1253 params_text = ''
1254 for param in command.params:
1255 params_text += '%s, ' % param.name
1256 params_text = params_text[:-2] + ');\n'
1257 cmdDef += ' skip |= manual_PreCallValidate'+ command.name[2:] + '(' + params_text
Mark Lobodzinski85672672016-10-13 08:36:42 -06001258 for line in lines:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001259 if type(line) is list:
1260 for sub in line:
1261 cmdDef += indent + sub
1262 else:
1263 cmdDef += indent + line
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001264 cmdDef += '%sreturn skip;\n' % indent
Mark Lobodzinski85672672016-10-13 08:36:42 -06001265 cmdDef += '}\n'
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001266 self.validation.append(cmdDef)