blob: e8f6dcc1535796646f49174a80c5ea0b1922e3b6 [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',
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700177 ]
178
Mark Lobodzinski85672672016-10-13 08:36:42 -0600179 # Commands to ignore
180 self.blacklist = [
181 'vkGetInstanceProcAddr',
182 'vkGetDeviceProcAddr',
Mark Young6ba8abe2017-11-09 10:37:04 -0700183 'vkEnumerateInstanceVersion',
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600184 'vkEnumerateInstanceLayerProperties',
185 'vkEnumerateInstanceExtensionProperties',
186 'vkEnumerateDeviceLayerProperties',
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600187 'vkEnumerateDeviceExtensionProperties',
188 ]
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700189
Dustin Gravesce68f082017-03-30 15:42:16 -0600190 # Structure fields to ignore
191 self.structMemberBlacklist = { 'VkWriteDescriptorSet' : ['dstSet'] }
Mark Lobodzinski85672672016-10-13 08:36:42 -0600192 # Validation conditions for some special case struct members that are conditionally validated
193 self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
194 # Header version
195 self.headerVersion = None
196 # Internal state - accumulators for different inner block text
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600197 self.validation = [] # Text comprising the main per-api parameter validation routines
Mark Lobodzinski85672672016-10-13 08:36:42 -0600198 self.stypes = [] # Values from the VkStructureType enumeration
199 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
200 self.handleTypes = set() # Set of handle type names
201 self.commands = [] # List of CommandData records for all Vulkan commands
202 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
203 self.validatedStructs = dict() # Map of structs type names to generated validation code for that struct type
204 self.enumRanges = dict() # Map of enum name to BEGIN/END range values
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600205 self.enumValueLists = '' # String containing enumerated type map definitions
Mark Lobodzinski85672672016-10-13 08:36:42 -0600206 self.flags = set() # Map of flags typenames
207 self.flagBits = dict() # Map of flag bits typename to list of values
Chris Forbes78ea32d2016-11-28 11:14:17 +1300208 self.newFlags = set() # Map of flags typenames /defined in the current feature/
Mike Schuchardtafd00482017-08-24 15:15:02 -0600209 self.required_extensions = dict() # Dictionary of required extensions for each item in the current extension
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600210 self.extension_type = '' # Type of active feature (extension), device or instance
211 self.extension_names = dict() # Dictionary of extension names to extension name defines
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600212 self.structextends_list = [] # List of extensions which extend another struct
213 self.struct_feature_protect = dict() # Dictionary of structnames and FeatureExtraProtect strings
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600214 self.valid_vuids = set() # Set of all valid VUIDs
Dave Houlton413a6782018-05-22 13:01:54 -0600215 self.vuid_dict = dict() # VUID dictionary (from JSON)
216 self.alias_dict = dict() # Dict of cmd|struct aliases
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700217 self.header_file = False # Header file generation flag
218 self.source_file = False # Source file generation flag
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600219 self.returnedonly_structs = []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600220 # Named tuples to store struct and command data
Mark Lobodzinski85672672016-10-13 08:36:42 -0600221 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600222 'isconst', 'isoptional', 'iscount', 'noautovalidity',
223 'len', 'extstructs', 'condition', 'cdecl'])
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600224 self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl', 'extension_type', 'result'])
Mark Lobodzinski85672672016-10-13 08:36:42 -0600225 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600226
Mark Lobodzinski85672672016-10-13 08:36:42 -0600227 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600228 # Generate Copyright comment block for file
229 def GenerateCopyright(self):
230 copyright = '/* *** THIS FILE IS GENERATED - DO NOT EDIT! ***\n'
231 copyright += ' * See parameter_validation_generator.py for modifications\n'
232 copyright += ' *\n'
Dave Houlton413a6782018-05-22 13:01:54 -0600233 copyright += ' * Copyright (c) 2015-2018 The Khronos Group Inc.\n'
234 copyright += ' * Copyright (c) 2015-2018 LunarG, Inc.\n'
235 copyright += ' * Copyright (C) 2015-2018 Google Inc.\n'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600236 copyright += ' *\n'
237 copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
238 copyright += ' * you may not use this file except in compliance with the License.\n'
239 copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n'
240 copyright += ' * You may obtain a copy of the License at\n'
241 copyright += ' *\n'
242 copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n'
243 copyright += ' *\n'
244 copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
245 copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
246 copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
247 copyright += ' * See the License for the specific language governing permissions and\n'
248 copyright += ' * limitations under the License.\n'
249 copyright += ' *\n'
250 copyright += ' * Author: Mark Lobodzinski <mark@LunarG.com>\n'
Dave Houlton413a6782018-05-22 13:01:54 -0600251 copyright += ' * Author: Dave Houlton <daveh@LunarG.com>\n'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600252 copyright += ' */\n\n'
253 return copyright
254 #
255 # Increases the global indent variable
Mark Lobodzinski85672672016-10-13 08:36:42 -0600256 def incIndent(self, indent):
257 inc = ' ' * self.INDENT_SPACES
258 if indent:
259 return indent + inc
260 return inc
261 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600262 # Decreases the global indent variable
Mark Lobodzinski85672672016-10-13 08:36:42 -0600263 def decIndent(self, indent):
264 if indent and (len(indent) > self.INDENT_SPACES):
265 return indent[:-self.INDENT_SPACES]
266 return ''
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600267 #
Dave Houlton413a6782018-05-22 13:01:54 -0600268 # Walk the JSON-derived dict and find all "vuid" key values
269 def ExtractVUIDs(self, d):
270 if hasattr(d, 'items'):
271 for k, v in d.items():
272 if k == "vuid":
273 yield v
274 elif isinstance(v, dict):
275 for s in self.ExtractVUIDs(v):
276 yield s
277 elif isinstance (v, list):
278 for l in v:
279 for s in self.ExtractVUIDs(l):
280 yield s
Mark Lobodzinski85672672016-10-13 08:36:42 -0600281 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600282 # Called at file creation time
Mark Lobodzinski85672672016-10-13 08:36:42 -0600283 def beginFile(self, genOpts):
284 OutputGenerator.beginFile(self, genOpts)
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700285 self.header_file = (genOpts.filename == 'parameter_validation.h')
286 self.source_file = (genOpts.filename == 'parameter_validation.cpp')
287
288 if not self.header_file and not self.source_file:
289 print("Error: Output Filenames have changed, update generator source.\n")
290 sys.exit(1)
291
292 if self.source_file or self.header_file:
293 # Output Copyright text
294 s = self.GenerateCopyright()
295 write(s, file=self.outFile)
296
297 if self.header_file:
298 return
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -0600299
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700300 # Build map of structure type names to VkStructureType enum values
301 # Find all types of category "struct"
302 for struct in self.registry.tree.iterfind('types/type[@category="struct"]'):
303 # Check if struct has member named "sType" of type "VkStructureType" which has values defined
304 stype = struct.find('member[name="sType"][type="VkStructureType"][@values]')
Shahbaz Youssefi23aee922019-01-11 14:04:49 -0500305 if stype is not None:
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700306 # Store VkStructureType value for this type
307 self.structTypes[struct.get('name')] = stype.get('values')
308
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -0600309 self.valid_usage_path = genOpts.valid_usage_path
310 vu_json_filename = os.path.join(self.valid_usage_path + os.sep, 'validusage.json')
311 if os.path.isfile(vu_json_filename):
312 json_file = open(vu_json_filename, 'r')
313 self.vuid_dict = json.load(json_file)
314 json_file.close()
315 if len(self.vuid_dict) == 0:
316 print("Error: Could not find, or error loading %s/validusage.json\n", vu_json_filename)
317 sys.exit(1)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600318 #
Dave Houlton413a6782018-05-22 13:01:54 -0600319 # Build a set of all vuid text strings found in validusage.json
320 for json_vuid_string in self.ExtractVUIDs(self.vuid_dict):
321 self.valid_vuids.add(json_vuid_string)
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600322 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600323 # Headers
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700324 write('#include "chassis.h"', file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600325 self.newline()
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700326 write('#include "stateless_validation.h"', file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600327 self.newline()
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600328 #
329 # Called at end-time for final content output
Mark Lobodzinski85672672016-10-13 08:36:42 -0600330 def endFile(self):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700331 if self.source_file:
332 # C-specific
333 self.newline()
334 write(self.enumValueLists, file=self.outFile)
335 self.newline()
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600336
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700337 pnext_handler = 'bool StatelessValidation::ValidatePnextStructContents(const char *api_name, const ParameterName &parameter_name, const GenericHeader* header) {\n'
338 pnext_handler += ' bool skip = false;\n'
339 pnext_handler += ' switch(header->sType) {\n'
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600340
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700341 # Do some processing here to extract data from validatedstructs...
342 for item in self.structextends_list:
343 postProcSpec = {}
344 postProcSpec['ppp'] = '' if not item else '{postProcPrefix}'
345 postProcSpec['pps'] = '' if not item else '{postProcSuffix}'
346 postProcSpec['ppi'] = '' if not item else '{postProcInsert}'
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600347
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700348 pnext_case = '\n'
349 protect = ''
350 # Guard struct cases with feature ifdefs, if necessary
351 if item in self.struct_feature_protect.keys():
352 protect = self.struct_feature_protect[item]
353 pnext_case += '#ifdef %s\n' % protect
354 pnext_case += ' // Validation code for %s structure members\n' % item
355 pnext_case += ' case %s: {\n' % self.structTypes[item]
356 pnext_case += ' %s *structure = (%s *) header;\n' % (item, item)
357 expr = self.expandStructCode(item, item, 'structure->', '', ' ', [], postProcSpec)
358 struct_validation_source = self.ScrubStructCode(expr)
359 pnext_case += '%s' % struct_validation_source
360 pnext_case += ' } break;\n'
361 if protect is not '':
362 pnext_case += '#endif // %s\n' % protect
363 # Skip functions containing no validation
364 if struct_validation_source != '':
365 pnext_handler += pnext_case;
366 pnext_handler += ' default:\n'
367 pnext_handler += ' skip = false;\n'
368 pnext_handler += ' }\n'
369 pnext_handler += ' return skip;\n'
370 pnext_handler += '}\n'
371 write(pnext_handler, file=self.outFile)
372 self.newline()
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600373
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700374 ext_template = 'bool StatelessValidation::OutputExtensionError(const std::string &api_name, const std::string &extension_name) {\n'
375 ext_template += ' return log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,\n'
376 ext_template += ' kVUID_PVError_ExtensionNotEnabled, "Attemped to call %s() but its required extension %s has not been enabled\\n",\n'
377 ext_template += ' api_name.c_str(), extension_name.c_str());\n'
378 ext_template += '}\n'
379 write(ext_template, file=self.outFile)
380 self.newline()
381 commands_text = '\n'.join(self.validation)
382 write(commands_text, file=self.outFile)
383 self.newline()
384 if self.header_file:
385 # Output declarations and record intercepted procedures
386 write('\n'.join(self.declarations), file=self.outFile)
387 # Finish processing in superclass
388 OutputGenerator.endFile(self)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600389 #
390 # Processing at beginning of each feature or extension
Mark Lobodzinski85672672016-10-13 08:36:42 -0600391 def beginFeature(self, interface, emit):
392 # Start processing in superclass
393 OutputGenerator.beginFeature(self, interface, emit)
394 # C-specific
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600395 # Accumulate includes, defines, types, enums, function pointer typedefs, end function prototypes separately for this
396 # feature. They're only printed in endFeature().
Mark Lobodzinski85672672016-10-13 08:36:42 -0600397 self.headerVersion = None
Mark Lobodzinski85672672016-10-13 08:36:42 -0600398 self.stypes = []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600399 self.commands = []
400 self.structMembers = []
Chris Forbes78ea32d2016-11-28 11:14:17 +1300401 self.newFlags = set()
Mark Lobodzinski62f71562017-10-24 13:41:18 -0600402 self.featureExtraProtect = GetFeatureProtect(interface)
Mike Schuchardtafd00482017-08-24 15:15:02 -0600403 # Get base list of extension dependencies for all items in this extension
404 base_required_extensions = []
Mark Lobodzinski31964ca2017-09-18 14:15:09 -0600405 if "VK_VERSION_1" not in self.featureName:
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600406 # Save Name Define to get correct enable name later
407 nameElem = interface[0][1]
408 name = nameElem.get('name')
409 self.extension_names[self.featureName] = name
410 # This extension is the first dependency for this command
Mike Schuchardtafd00482017-08-24 15:15:02 -0600411 base_required_extensions.append(self.featureName)
412 # Add any defined extension dependencies to the base dependency list for this extension
413 requires = interface.get('requires')
414 if requires is not None:
415 base_required_extensions.extend(requires.split(','))
Mike Schuchardtafd00482017-08-24 15:15:02 -0600416 # Build dictionary of extension dependencies for each item in this extension
417 self.required_extensions = dict()
418 for require_element in interface.findall('require'):
419 # Copy base extension dependency list
420 required_extensions = list(base_required_extensions)
421 # Add any additional extension dependencies specified in this require block
422 additional_extensions = require_element.get('extension')
423 if additional_extensions:
424 required_extensions.extend(additional_extensions.split(','))
425 # Save full extension list for all named items
426 for element in require_element.findall('*[@name]'):
427 self.required_extensions[element.get('name')] = required_extensions
428
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600429 # And note if this is an Instance or Device extension
430 self.extension_type = interface.get('type')
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600431 #
432 # Called at the end of each extension (feature)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600433 def endFeature(self):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700434 if self.header_file:
435 return
Mark Lobodzinski85672672016-10-13 08:36:42 -0600436 # C-specific
437 # Actually write the interface to the output file.
438 if (self.emit):
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600439 # 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 -0600440 # or move it below the 'for section...' loop.
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600441 ifdef = ''
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100442 if (self.featureExtraProtect is not None):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600443 ifdef = '#ifdef %s\n' % self.featureExtraProtect
444 self.validation.append(ifdef)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600445 # Generate the struct member checking code from the captured data
446 self.processStructMemberData()
447 # Generate the command parameter checking code from the captured data
448 self.processCmdData()
449 # Write the declaration for the HeaderVersion
450 if self.headerVersion:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700451 write('const uint32_t GeneratedVulkanHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600452 self.newline()
453 # Write the declarations for the VkFlags values combining all flag bits
Chris Forbes78ea32d2016-11-28 11:14:17 +1300454 for flag in sorted(self.newFlags):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600455 flagBits = flag.replace('Flags', 'FlagBits')
456 if flagBits in self.flagBits:
457 bits = self.flagBits[flagBits]
458 decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
459 for bit in bits[1:]:
460 decl += '|' + bit
461 decl += ';'
462 write(decl, file=self.outFile)
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600463 endif = '\n'
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100464 if (self.featureExtraProtect is not None):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600465 endif = '#endif // %s\n' % self.featureExtraProtect
466 self.validation.append(endif)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600467 # Finish processing in superclass
468 OutputGenerator.endFeature(self)
469 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600470 # Type generation
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700471 def genType(self, typeinfo, name, alias):
Dave Houlton413a6782018-05-22 13:01:54 -0600472 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100473 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600474 self.alias_dict[name]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700475 OutputGenerator.genType(self, typeinfo, name, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600476 typeElem = typeinfo.elem
Mark Lobodzinski87017df2018-05-30 11:29:24 -0600477 # 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 -0600478 category = typeElem.get('category')
479 if (category == 'struct' or category == 'union'):
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700480 self.genStruct(typeinfo, name, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600481 elif (category == 'handle'):
482 self.handleTypes.add(name)
483 elif (category == 'bitmask'):
484 self.flags.add(name)
Chris Forbes78ea32d2016-11-28 11:14:17 +1300485 self.newFlags.add(name)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600486 elif (category == 'define'):
487 if name == 'VK_HEADER_VERSION':
488 nameElem = typeElem.find('name')
489 self.headerVersion = noneStr(nameElem.tail).strip()
490 #
491 # Struct parameter check generation.
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600492 # This is a special case of the <type> tag where the contents are interpreted as a set of <member> tags instead of freeform C
493 # type declarations. The <member> tags are just like <param> tags - they are a declaration of a struct or union member.
494 # Only simple member declarations are supported (no nested structs etc.)
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700495 def genStruct(self, typeinfo, typeName, alias):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700496 if not self.source_file:
497 return
Dave Houlton413a6782018-05-22 13:01:54 -0600498 # alias has already been recorded in genType, above
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700499 OutputGenerator.genStruct(self, typeinfo, typeName, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600500 conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
501 members = typeinfo.elem.findall('.//member')
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600502 if self.featureExtraProtect is not None:
503 self.struct_feature_protect[typeName] = self.featureExtraProtect
Mark Lobodzinski85672672016-10-13 08:36:42 -0600504 #
505 # Iterate over members once to get length parameters for arrays
506 lens = set()
507 for member in members:
508 len = self.getLen(member)
509 if len:
510 lens.add(len)
511 #
512 # Generate member info
513 membersInfo = []
514 for member in members:
515 # Get the member's type and name
516 info = self.getTypeNameTuple(member)
517 type = info[0]
518 name = info[1]
519 stypeValue = ''
520 cdecl = self.makeCParamDecl(member, 0)
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700521
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600522 # Store pointer/array/string info -- Check for parameter name in lens set
Mark Lobodzinski85672672016-10-13 08:36:42 -0600523 iscount = False
524 if name in lens:
525 iscount = True
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600526 # The pNext members are not tagged as optional, but are treated as optional for parameter NULL checks. Static array
527 # members are also treated as optional to skip NULL pointer validation, as they won't be NULL.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600528 isstaticarray = self.paramIsStaticArray(member)
529 isoptional = False
530 if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
531 isoptional = True
Dustin Gravesce68f082017-03-30 15:42:16 -0600532 # Determine if value should be ignored by code generation.
533 noautovalidity = False
534 if (member.attrib.get('noautovalidity') is not None) or ((typeName in self.structMemberBlacklist) and (name in self.structMemberBlacklist[typeName])):
535 noautovalidity = True
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600536 structextends = False
Mark Lobodzinski85672672016-10-13 08:36:42 -0600537 membersInfo.append(self.CommandParam(type=type, name=name,
538 ispointer=self.paramIsPointer(member),
539 isstaticarray=isstaticarray,
540 isbool=True if type == 'VkBool32' else False,
541 israngedenum=True if type in self.enumRanges else False,
542 isconst=True if 'const' in cdecl else False,
543 isoptional=isoptional,
544 iscount=iscount,
Dustin Gravesce68f082017-03-30 15:42:16 -0600545 noautovalidity=noautovalidity,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600546 len=self.getLen(member),
Mike Schuchardta40d0b02017-07-23 12:47:47 -0600547 extstructs=self.registry.validextensionstructs[typeName] if name == 'pNext' else None,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600548 condition=conditions[name] if conditions and name in conditions else None,
549 cdecl=cdecl))
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600550 # If this struct extends another, keep its name in list for further processing
551 if typeinfo.elem.attrib.get('structextends') is not None:
552 self.structextends_list.append(typeName)
553 # Returnedonly structs should have most of their members ignored -- on entry, we only care about validating the sType and
554 # pNext members. Everything else will be overwritten by the callee.
555 if typeinfo.elem.attrib.get('returnedonly') is not None:
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600556 self.returnedonly_structs.append(typeName)
557 membersInfo = [m for m in membersInfo if m.name in ('sType', 'pNext')]
Mark Lobodzinski85672672016-10-13 08:36:42 -0600558 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
559 #
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600560 # Capture group (e.g. C "enum" type) info to be used for param check code generation.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600561 # These are concatenated together with other types.
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700562 def genGroup(self, groupinfo, groupName, alias):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700563 if not self.source_file:
564 return
Dave Houlton413a6782018-05-22 13:01:54 -0600565 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100566 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600567 self.alias_dict[groupName]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700568 OutputGenerator.genGroup(self, groupinfo, groupName, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600569 groupElem = groupinfo.elem
Mark Lobodzinski85672672016-10-13 08:36:42 -0600570 # Store the sType values
571 if groupName == 'VkStructureType':
572 for elem in groupElem.findall('enum'):
573 self.stypes.append(elem.get('name'))
574 elif 'FlagBits' in groupName:
575 bits = []
576 for elem in groupElem.findall('enum'):
Shannon McPherson533a66c2018-08-21 12:09:25 -0600577 if elem.get('supported') != 'disabled':
578 bits.append(elem.get('name'))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600579 if bits:
580 self.flagBits[groupName] = bits
581 else:
582 # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
583 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
584 expandPrefix = expandName
585 expandSuffix = ''
586 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
587 if expandSuffixMatch:
588 expandSuffix = '_' + expandSuffixMatch.group()
589 # Strip off the suffix from the prefix
590 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
591 isEnum = ('FLAG_BITS' not in expandPrefix)
592 if isEnum:
593 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600594 # Create definition for a list containing valid enum values for this enumerated type
595 enum_entry = 'const std::vector<%s> All%sEnums = {' % (groupName, groupName)
596 for enum in groupElem:
597 name = enum.get('name')
Mark Lobodzinski117d88f2017-07-27 12:09:08 -0600598 if name is not None and enum.get('supported') != 'disabled':
599 enum_entry += '%s, ' % name
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600600 enum_entry += '};\n'
601 self.enumValueLists += enum_entry
Mark Lobodzinski85672672016-10-13 08:36:42 -0600602 #
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600603 # Capture command parameter info to be used for param check code generation.
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700604 def genCmd(self, cmdinfo, name, alias):
Dave Houlton413a6782018-05-22 13:01:54 -0600605 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100606 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600607 self.alias_dict[name]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700608 OutputGenerator.genCmd(self, cmdinfo, name, alias)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600609 decls = self.makeCDecls(cmdinfo.elem)
610 typedef = decls[1]
611 typedef = typedef.split(')',1)[1]
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700612 if self.header_file:
613 if name not in self.blacklist:
614 if (self.featureExtraProtect is not None):
615 self.declarations += [ '#ifdef %s' % self.featureExtraProtect ]
616 # Strip off 'vk' from API name
617 self.declarations += [ '%s%s' % ('bool PreCallValidate', decls[0].split("VKAPI_CALL vk")[1])]
618 if (self.featureExtraProtect is not None):
619 self.declarations += [ '#endif' ]
620 if self.source_file:
621 if name not in self.blacklist:
622 params = cmdinfo.elem.findall('param')
623 # Get list of array lengths
624 lens = set()
625 for param in params:
626 len = self.getLen(param)
627 if len:
628 lens.add(len)
629 # Get param info
630 paramsInfo = []
631 for param in params:
632 paramInfo = self.getTypeNameTuple(param)
633 cdecl = self.makeCParamDecl(param, 0)
634 # Check for parameter name in lens set
635 iscount = False
636 if paramInfo[1] in lens:
637 iscount = True
638 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
639 ispointer=self.paramIsPointer(param),
640 isstaticarray=self.paramIsStaticArray(param),
641 isbool=True if paramInfo[0] == 'VkBool32' else False,
642 israngedenum=True if paramInfo[0] in self.enumRanges else False,
643 isconst=True if 'const' in cdecl else False,
644 isoptional=self.paramIsOptional(param),
645 iscount=iscount,
646 noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
647 len=self.getLen(param),
648 extstructs=None,
649 condition=None,
650 cdecl=cdecl))
651 # Save return value information, if any
652 result_type = ''
653 resultinfo = cmdinfo.elem.find('proto/type')
654 if (resultinfo is not None and resultinfo.text != 'void'):
655 result_type = resultinfo.text
656 self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0], extension_type=self.extension_type, result=result_type))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600657 #
658 # Check if the parameter passed in is a pointer
659 def paramIsPointer(self, param):
660 ispointer = 0
661 paramtype = param.find('type')
662 if (paramtype.tail is not None) and ('*' in paramtype.tail):
663 ispointer = paramtype.tail.count('*')
664 elif paramtype.text[:4] == 'PFN_':
665 # Treat function pointer typedefs as a pointer to a single value
666 ispointer = 1
667 return ispointer
668 #
669 # Check if the parameter passed in is a static array
670 def paramIsStaticArray(self, param):
671 isstaticarray = 0
672 paramname = param.find('name')
673 if (paramname.tail is not None) and ('[' in paramname.tail):
674 isstaticarray = paramname.tail.count('[')
675 return isstaticarray
676 #
677 # Check if the parameter passed in is optional
678 # Returns a list of Boolean values for comma separated len attributes (len='false,true')
679 def paramIsOptional(self, param):
680 # See if the handle is optional
681 isoptional = False
682 # Simple, if it's optional, return true
683 optString = param.attrib.get('optional')
684 if optString:
685 if optString == 'true':
686 isoptional = True
687 elif ',' in optString:
688 opts = []
689 for opt in optString.split(','):
690 val = opt.strip()
691 if val == 'true':
692 opts.append(True)
693 elif val == 'false':
694 opts.append(False)
695 else:
696 print('Unrecognized len attribute value',val)
697 isoptional = opts
698 return isoptional
699 #
700 # Check if the handle passed in is optional
701 # Uses the same logic as ValidityOutputGenerator.isHandleOptional
702 def isHandleOptional(self, param, lenParam):
703 # Simple, if it's optional, return true
704 if param.isoptional:
705 return True
706 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
707 if param.noautovalidity:
708 return True
709 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
710 if lenParam and lenParam.isoptional:
711 return True
712 return False
713 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600714 # Retrieve the value of the len tag
715 def getLen(self, param):
716 result = None
717 len = param.attrib.get('len')
718 if len and len != 'null-terminated':
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600719 # For string arrays, 'len' can look like 'count,null-terminated', indicating that we have a null terminated array of
720 # strings. We strip the null-terminated from the 'len' field and only return the parameter specifying the string count
Mark Lobodzinski85672672016-10-13 08:36:42 -0600721 if 'null-terminated' in len:
722 result = len.split(',')[0]
723 else:
724 result = len
725 result = str(result).replace('::', '->')
726 return result
727 #
728 # Retrieve the type and name for a parameter
729 def getTypeNameTuple(self, param):
730 type = ''
731 name = ''
732 for elem in param:
733 if elem.tag == 'type':
734 type = noneStr(elem.text)
735 elif elem.tag == 'name':
736 name = noneStr(elem.text)
737 return (type, name)
738 #
739 # Find a named parameter in a parameter list
740 def getParamByName(self, params, name):
741 for param in params:
742 if param.name == name:
743 return param
744 return None
745 #
746 # Extract length values from latexmath. Currently an inflexible solution that looks for specific
747 # patterns that are found in vk.xml. Will need to be updated when new patterns are introduced.
748 def parseLateXMath(self, source):
749 name = 'ERROR'
750 decoratedName = 'ERROR'
751 if 'mathit' in source:
Mark Lobodzinski36c33862017-02-13 10:15:53 -0700752 # Matches expressions similar to 'latexmath:[\lceil{\mathit{rasterizationSamples} \over 32}\rceil]'
753 match = re.match(r'latexmath\s*\:\s*\[\s*\\l(\w+)\s*\{\s*\\mathit\s*\{\s*(\w+)\s*\}\s*\\over\s*(\d+)\s*\}\s*\\r(\w+)\s*\]', source)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600754 if not match or match.group(1) != match.group(4):
755 raise 'Unrecognized latexmath expression'
756 name = match.group(2)
757 decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
758 else:
Mark Lobodzinski36c33862017-02-13 10:15:53 -0700759 # Matches expressions similar to 'latexmath : [dataSize \over 4]'
Shannon McPhersonbd68df02018-10-29 15:04:41 -0600760 match = re.match(r'latexmath\s*\:\s*\[\s*(\\textrm\{)?(\w+)\}?\s*\\over\s*(\d+)\s*\]', source)
761 name = match.group(2)
762 decoratedName = '{}/{}'.format(*match.group(2, 3))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600763 return name, decoratedName
764 #
765 # Get the length paramater record for the specified parameter name
766 def getLenParam(self, params, name):
767 lenParam = None
768 if name:
769 if '->' in name:
770 # The count is obtained by dereferencing a member of a struct parameter
771 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600772 isstaticarray=None, isoptional=False, type=None, noautovalidity=False,
773 len=None, extstructs=None, condition=None, cdecl=None)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600774 elif 'latexmath' in name:
775 lenName, decoratedName = self.parseLateXMath(name)
776 lenParam = self.getParamByName(params, lenName)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600777 else:
778 lenParam = self.getParamByName(params, name)
779 return lenParam
780 #
781 # Convert a vulkan.h command declaration into a parameter_validation.h definition
782 def getCmdDef(self, cmd):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600783 # Strip the trailing ';' and split into individual lines
784 lines = cmd.cdecl[:-1].split('\n')
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600785 cmd_hdr = '\n'.join(lines)
786 return cmd_hdr
Mark Lobodzinski85672672016-10-13 08:36:42 -0600787 #
788 # Generate the code to check for a NULL dereference before calling the
789 # validation function
790 def genCheckedLengthCall(self, name, exprs):
791 count = name.count('->')
792 if count:
793 checkedExpr = []
794 localIndent = ''
795 elements = name.split('->')
796 # Open the if expression blocks
797 for i in range(0, count):
798 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
799 localIndent = self.incIndent(localIndent)
800 # Add the validation expression
801 for expr in exprs:
802 checkedExpr.append(localIndent + expr)
803 # Close the if blocks
804 for i in range(0, count):
805 localIndent = self.decIndent(localIndent)
806 checkedExpr.append(localIndent + '}\n')
807 return [checkedExpr]
808 # No if statements were required
809 return exprs
810 #
811 # Generate code to check for a specific condition before executing validation code
812 def genConditionalCall(self, prefix, condition, exprs):
813 checkedExpr = []
814 localIndent = ''
815 formattedCondition = condition.format(prefix)
816 checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
817 checkedExpr.append(localIndent + '{\n')
818 localIndent = self.incIndent(localIndent)
819 for expr in exprs:
820 checkedExpr.append(localIndent + expr)
821 localIndent = self.decIndent(localIndent)
822 checkedExpr.append(localIndent + '}\n')
823 return [checkedExpr]
824 #
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600825 # Get VUID identifier from implicit VUID tag
Dave Houlton413a6782018-05-22 13:01:54 -0600826 def GetVuid(self, name, suffix):
827 vuid_string = 'VUID-%s-%s' % (name, suffix)
828 vuid = "kVUIDUndefined"
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600829 if '->' in vuid_string:
Dave Houlton413a6782018-05-22 13:01:54 -0600830 return vuid
831 if vuid_string in self.valid_vuids:
832 vuid = "\"%s\"" % vuid_string
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600833 else:
Dave Houlton413a6782018-05-22 13:01:54 -0600834 if name in self.alias_dict:
835 alias_string = 'VUID-%s-%s' % (self.alias_dict[name], suffix)
836 if alias_string in self.valid_vuids:
Shannon McPherson23d97212019-02-18 13:39:42 -0700837 vuid = "\"%s\"" % alias_string
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600838 return vuid
839 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600840 # Generate the sType check string
Mark Lobodzinski9cf24dd2017-06-28 14:23:22 -0600841 def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600842 checkExpr = []
843 stype = self.structTypes[value.type]
Mark Lobodzinski59603552018-05-29 16:14:59 -0600844 vuid_name = struct_type_name if struct_type_name is not None else funcPrintName
845 stype_vuid = self.GetVuid(value.type, "sType-sType")
846 param_vuid = self.GetVuid(vuid_name, "%s-parameter" % value.name)
847
Mark Lobodzinski85672672016-10-13 08:36:42 -0600848 if lenValue:
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800849 count_required_vuid = self.GetVuid(vuid_name, "%s-arraylength" % lenValue.name)
850
Mark Lobodzinski85672672016-10-13 08:36:42 -0600851 # This is an array with a pointer to a count value
852 if lenValue.ispointer:
853 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800854 checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {}, {});\n'.format(
855 funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, stype_vuid, param_vuid, count_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype, pf=prefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600856 # This is an array with an integer count value
857 else:
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800858 checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {});\n'.format(
859 funcPrintName, lenValueRequired, valueRequired, stype_vuid, param_vuid, count_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype, pf=prefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600860 # This is an individual struct
861 else:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700862 checkExpr.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {}, {}, {});\n'.format(
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700863 funcPrintName, valuePrintName, prefix, valueRequired, param_vuid, stype_vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600864 return checkExpr
865 #
866 # Generate the handle check string
867 def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
868 checkExpr = []
869 if lenValue:
870 if lenValue.ispointer:
871 # This is assumed to be an output array with a pointer to a count value
872 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
873 else:
874 # This is an array with an integer count value
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700875 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 -0600876 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
877 else:
878 # This is assumed to be an output handle pointer
879 raise('Unsupported parameter validation case: Output handles are not NULL checked')
880 return checkExpr
881 #
882 # Generate check string for an array of VkFlags values
883 def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
884 checkExpr = []
885 flagBitsName = value.type.replace('Flags', 'FlagBits')
886 if not flagBitsName in self.flagBits:
887 raise('Unsupported parameter validation case: array of reserved VkFlags')
888 else:
889 allFlags = 'All' + flagBitsName
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700890 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 -0600891 return checkExpr
892 #
893 # Generate pNext check string
Mark Lobodzinski3c828522017-06-26 13:05:57 -0600894 def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600895 checkExpr = []
896 # Generate an array of acceptable VkStructureType values for pNext
897 extStructCount = 0
898 extStructVar = 'NULL'
899 extStructNames = 'NULL'
Dave Houlton413a6782018-05-22 13:01:54 -0600900 vuid = self.GetVuid(struct_type_name, "pNext-pNext")
Mark Lobodzinski85672672016-10-13 08:36:42 -0600901 if value.extstructs:
Mike Schuchardtc73d07e2017-07-12 10:10:01 -0600902 extStructVar = 'allowed_structs_{}'.format(struct_type_name)
903 extStructCount = 'ARRAY_SIZE({})'.format(extStructVar)
Mike Schuchardta40d0b02017-07-23 12:47:47 -0600904 extStructNames = '"' + ', '.join(value.extstructs) + '"'
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700905 checkExpr.append('const VkStructureType {}[] = {{ {} }};\n'.format(extStructVar, ', '.join([self.structTypes[s] for s in value.extstructs])))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700906 checkExpr.append('skip |= validate_struct_pnext("{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedVulkanHeaderVersion, {});\n'.format(
Mark Lobodzinski3c828522017-06-26 13:05:57 -0600907 funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, vuid, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600908 return checkExpr
909 #
910 # Generate the pointer check string
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600911 def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600912 checkExpr = []
Mark Lobodzinskidead0b62017-06-28 13:22:03 -0600913 vuid_tag_name = struct_type_name if struct_type_name is not None else funcPrintName
Mark Lobodzinski85672672016-10-13 08:36:42 -0600914 if lenValue:
Dave Houlton413a6782018-05-22 13:01:54 -0600915 count_required_vuid = self.GetVuid(vuid_tag_name, "%s-arraylength" % (lenValue.name))
916 array_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600917 # This is an array with a pointer to a count value
918 if lenValue.ispointer:
919 # If count and array parameters are optional, there will be no validation
920 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
921 # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700922 checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600923 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 -0600924 # This is an array with an integer count value
925 else:
926 # If count and array parameters are optional, there will be no validation
927 if valueRequired == 'true' or lenValueRequired == 'true':
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600928 if value.type != 'char':
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700929 checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600930 funcPrintName, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
931 else:
932 # Arrays of strings receive special processing
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700933 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 -0600934 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 -0600935 if checkExpr:
936 if lenValue and ('->' in lenValue.name):
937 # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
938 checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
939 # This is an individual struct that is not allowed to be NULL
940 elif not value.isoptional:
941 # Function pointers need a reinterpret_cast to void*
Dave Houlton413a6782018-05-22 13:01:54 -0600942 ptr_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600943 if value.type[:4] == 'PFN_':
Dave Houlton413a6782018-05-22 13:01:54 -0600944 allocator_dict = {'pfnAllocation': '"VUID-VkAllocationCallbacks-pfnAllocation-00632"',
945 'pfnReallocation': '"VUID-VkAllocationCallbacks-pfnReallocation-00633"',
946 'pfnFree': '"VUID-VkAllocationCallbacks-pfnFree-00634"',
947 'pfnInternalAllocation': '"VUID-VkAllocationCallbacks-pfnInternalAllocation-00635"'
Mark Lobodzinski02fa1972017-06-28 14:46:14 -0600948 }
949 vuid = allocator_dict.get(value.name)
950 if vuid is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600951 ptr_required_vuid = vuid
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700952 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 -0600953 else:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700954 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 -0600955 return checkExpr
956 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -0600957 # Process struct member validation code, performing name substitution if required
Mark Lobodzinski85672672016-10-13 08:36:42 -0600958 def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
959 # Build format specifier list
960 kwargs = {}
961 if '{postProcPrefix}' in line:
962 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
963 if type(memberDisplayNamePrefix) is tuple:
964 kwargs['postProcPrefix'] = 'ParameterName('
965 else:
966 kwargs['postProcPrefix'] = postProcSpec['ppp']
967 if '{postProcSuffix}' in line:
968 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
969 if type(memberDisplayNamePrefix) is tuple:
970 kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
971 else:
972 kwargs['postProcSuffix'] = postProcSpec['pps']
973 if '{postProcInsert}' in line:
974 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
975 if type(memberDisplayNamePrefix) is tuple:
976 kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
977 else:
978 kwargs['postProcInsert'] = postProcSpec['ppi']
979 if '{funcName}' in line:
980 kwargs['funcName'] = funcName
981 if '{valuePrefix}' in line:
982 kwargs['valuePrefix'] = memberNamePrefix
983 if '{displayNamePrefix}' in line:
984 # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
985 if type(memberDisplayNamePrefix) is tuple:
986 kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
987 else:
988 kwargs['displayNamePrefix'] = memberDisplayNamePrefix
989
990 if kwargs:
991 # Need to escape the C++ curly braces
992 if 'IndexVector' in line:
993 line = line.replace('IndexVector{ ', 'IndexVector{{ ')
994 line = line.replace(' }),', ' }}),')
995 return line.format(**kwargs)
996 return line
997 #
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600998 # Process struct member validation code, stripping metadata
999 def ScrubStructCode(self, code):
1000 scrubbed_lines = ''
1001 for line in code:
1002 if 'validate_struct_pnext' in line:
1003 continue
1004 if 'allowed_structs' in line:
1005 continue
1006 if 'xml-driven validation' in line:
1007 continue
1008 line = line.replace('{postProcPrefix}', '')
1009 line = line.replace('{postProcSuffix}', '')
1010 line = line.replace('{postProcInsert}', '')
1011 line = line.replace('{funcName}', '')
1012 line = line.replace('{valuePrefix}', '')
1013 line = line.replace('{displayNamePrefix}', '')
1014 line = line.replace('{IndexVector}', '')
1015 line = line.replace('local_data->', '')
1016 scrubbed_lines += line
1017 return scrubbed_lines
1018 #
Mark Lobodzinski85672672016-10-13 08:36:42 -06001019 # Process struct validation code for inclusion in function or parent struct validation code
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001020 def expandStructCode(self, item_type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
1021 lines = self.validatedStructs[item_type]
Mark Lobodzinski85672672016-10-13 08:36:42 -06001022 for line in lines:
1023 if output:
1024 output[-1] += '\n'
1025 if type(line) is list:
1026 for sub in line:
1027 output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1028 else:
1029 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1030 return output
1031 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001032 # Process struct pointer/array validation code, performing name substitution if required
Mark Lobodzinski85672672016-10-13 08:36:42 -06001033 def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
1034 expr = []
1035 expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
1036 expr.append('{')
1037 indent = self.incIndent(None)
1038 if lenValue:
1039 # Need to process all elements in the array
1040 indexName = lenValue.name.replace('Count', 'Index')
1041 expr[-1] += '\n'
Mark Young39389872017-01-19 21:10:49 -07001042 if lenValue.ispointer:
1043 # If the length value is a pointer, de-reference it for the count.
1044 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < *{}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
1045 else:
1046 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001047 expr.append(indent + '{')
1048 indent = self.incIndent(indent)
1049 # Prefix for value name to display in error message
Mark Lobodzinski6f82eb52016-12-05 07:38:41 -07001050 if value.ispointer == 2:
1051 memberNamePrefix = '{}{}[{}]->'.format(prefix, value.name, indexName)
1052 memberDisplayNamePrefix = ('{}[%i]->'.format(valueDisplayName), indexName)
1053 else:
1054 memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
1055 memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001056 else:
1057 memberNamePrefix = '{}{}->'.format(prefix, value.name)
1058 memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001059 # Expand the struct validation lines
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001060 expr = self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001061 if lenValue:
1062 # Close if and for scopes
1063 indent = self.decIndent(indent)
1064 expr.append(indent + '}\n')
1065 expr.append('}\n')
1066 return expr
1067 #
1068 # Generate the parameter checking code
1069 def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
1070 lines = [] # Generated lines of code
1071 unused = [] # Unused variable names
1072 for value in values:
1073 usedLines = []
1074 lenParam = None
1075 #
1076 # Prefix and suffix for post processing of parameter names for struct members. Arrays of structures need special processing to include the array index in the full parameter name.
1077 postProcSpec = {}
1078 postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
1079 postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
1080 postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
1081 #
1082 # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name
1083 valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
1084 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001085 # Check for NULL pointers, ignore the in-out count parameters that
Mark Lobodzinski85672672016-10-13 08:36:42 -06001086 # will be validated with their associated array
1087 if (value.ispointer or value.isstaticarray) and not value.iscount:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001088 # Parameters for function argument generation
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001089 req = 'true' # Parameter cannot be NULL
Mark Lobodzinski85672672016-10-13 08:36:42 -06001090 cpReq = 'true' # Count pointer cannot be NULL
1091 cvReq = 'true' # Count value cannot be 0
1092 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
Mark Lobodzinski85672672016-10-13 08:36:42 -06001093 # Generate required/optional parameter strings for the pointer and count values
1094 if value.isoptional:
1095 req = 'false'
1096 if value.len:
1097 # The parameter is an array with an explicit count parameter
1098 lenParam = self.getLenParam(values, value.len)
1099 lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
1100 if lenParam.ispointer:
1101 # Count parameters that are pointers are inout
1102 if type(lenParam.isoptional) is list:
1103 if lenParam.isoptional[0]:
1104 cpReq = 'false'
1105 if lenParam.isoptional[1]:
1106 cvReq = 'false'
1107 else:
1108 if lenParam.isoptional:
1109 cpReq = 'false'
1110 else:
1111 if lenParam.isoptional:
1112 cvReq = 'false'
1113 #
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001114 # The parameter will not be processed when tagged as 'noautovalidity'
Mark Lobodzinski85672672016-10-13 08:36:42 -06001115 # For the pointer to struct case, the struct pointer will not be validated, but any
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001116 # members not tagged as 'noautovalidity' will be validated
1117 # We special-case the custom allocator checks, as they are explicit but can be auto-generated.
1118 AllocatorFunctions = ['PFN_vkAllocationFunction', 'PFN_vkReallocationFunction', 'PFN_vkFreeFunction']
1119 if value.noautovalidity and value.type not in AllocatorFunctions:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001120 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1121 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1122 else:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001123 if value.type in self.structTypes:
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001124 # If this is a pointer to a struct with an sType field, verify the type
Mark Lobodzinski9cf24dd2017-06-28 14:23:22 -06001125 usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001126 # If this is an input handle array that is not allowed to contain NULL handles, verify that none of the handles are VK_NULL_HANDLE
1127 elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
1128 usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1129 elif value.type in self.flags and value.isconst:
1130 usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1131 elif value.isbool and value.isconst:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001132 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 -06001133 elif value.israngedenum and value.isconst:
Mark Lobodzinskiaff801e2017-07-25 15:29:57 -06001134 enum_value_list = 'All%sEnums' % value.type
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001135 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 -07001136 elif value.name == 'pNext' and value.isconst:
Mark Lobodzinski9ddf9282018-05-31 13:59:59 -06001137 usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001138 else:
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -06001139 usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001140 # 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 -06001141 if value.type in self.validatedStructs:
1142 if value.isconst: # or value.type in self.returnedonly_structs:
1143 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
1144 elif value.type in self.returnedonly_structs:
1145 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001146 # Non-pointer types
1147 else:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001148 # The parameter will not be processes when tagged as 'noautovalidity'
1149 # For the struct case, the struct type will not be validated, but any
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001150 # members not tagged as 'noautovalidity' will be validated
Mark Lobodzinski85672672016-10-13 08:36:42 -06001151 if value.noautovalidity:
1152 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1153 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1154 else:
Mark Lobodzinski024b2822017-06-27 13:22:05 -06001155 vuid_name_tag = structTypeName if structTypeName is not None else funcName
Mark Lobodzinski85672672016-10-13 08:36:42 -06001156 if value.type in self.structTypes:
1157 stype = self.structTypes[value.type]
Dave Houlton413a6782018-05-22 13:01:54 -06001158 vuid = self.GetVuid(value.type, "sType-sType")
Mark Lobodzinskia16ebc72018-06-15 14:47:39 -06001159 undefined_vuid = '"kVUIDUndefined"'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001160 usedLines.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false, kVUIDUndefined, {});\n'.format(
Mike Schuchardt24ac4e72018-08-11 17:37:20 -07001161 funcName, valueDisplayName, valuePrefix, vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001162 elif value.type in self.handleTypes:
1163 if not self.isHandleOptional(value, None):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001164 usedLines.append('skip |= validate_required_handle("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001165 elif value.type in self.flags:
1166 flagBitsName = value.type.replace('Flags', 'FlagBits')
1167 if not flagBitsName in self.flagBits:
Dave Houlton413a6782018-05-22 13:01:54 -06001168 vuid = self.GetVuid(vuid_name_tag, "%s-zerobitmask" % (value.name))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001169 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 -06001170 else:
Mark Lobodzinskie643fcc2017-06-26 16:27:15 -06001171 if value.isoptional:
1172 flagsRequired = 'false'
Dave Houlton413a6782018-05-22 13:01:54 -06001173 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinskie643fcc2017-06-26 16:27:15 -06001174 else:
1175 flagsRequired = 'true'
Dave Houlton413a6782018-05-22 13:01:54 -06001176 vuid = self.GetVuid(vuid_name_tag, "%s-requiredbitmask" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001177 allFlagsName = 'All' + flagBitsName
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001178 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 -06001179 elif value.type in self.flagBits:
1180 flagsRequired = 'false' if value.isoptional else 'true'
1181 allFlagsName = 'All' + value.type
Dave Houlton413a6782018-05-22 13:01:54 -06001182 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001183 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 -06001184 elif value.isbool:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001185 usedLines.append('skip |= validate_bool32("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001186 elif value.israngedenum:
Dave Houlton413a6782018-05-22 13:01:54 -06001187 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinski74cb45f2017-07-25 15:10:29 -06001188 enum_value_list = 'All%sEnums' % value.type
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001189 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 -06001190 # If this is a struct, see if it contains members that need to be checked
1191 if value.type in self.validatedStructs:
1192 memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
1193 memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001194 usedLines.append(self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001195 # Append the parameter check to the function body for the current command
1196 if usedLines:
1197 # Apply special conditional checks
1198 if value.condition:
1199 usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
1200 lines += usedLines
1201 elif not value.iscount:
1202 # If no expression was generated for this value, it is unreferenced by the validation function, unless
1203 # it is an array count, which is indirectly referenced for array valiadation.
1204 unused.append(value.name)
Mark Lobodzinskid4950072017-08-01 13:02:20 -06001205 if not lines:
1206 lines.append('// No xml-driven validation\n')
Mark Lobodzinski85672672016-10-13 08:36:42 -06001207 return lines, unused
1208 #
1209 # Generate the struct member check code from the captured data
1210 def processStructMemberData(self):
1211 indent = self.incIndent(None)
1212 for struct in self.structMembers:
1213 #
1214 # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
1215 lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
1216 if lines:
1217 self.validatedStructs[struct.name] = lines
1218 #
1219 # Generate the command param check code from the captured data
1220 def processCmdData(self):
1221 indent = self.incIndent(None)
1222 for command in self.commands:
1223 # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
1224 startIndex = 0 if command.name == 'vkCreateInstance' else 1
1225 lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
Mark Lobodzinski3f10bfe2017-08-23 15:23:23 -06001226 # Cannot validate extension dependencies for device extension APIs having a physical device as their dispatchable object
Mike Schuchardtafd00482017-08-24 15:15:02 -06001227 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 -06001228 ext_test = ''
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001229 if command.params[0].type in ["VkInstance", "VkPhysicalDevice"] or command.name == 'vkCreateInstance':
1230 ext_table_type = 'instance'
1231 else:
1232 ext_table_type = 'device'
Mike Schuchardtafd00482017-08-24 15:15:02 -06001233 for ext in self.required_extensions[command.name]:
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001234 ext_name_define = ''
1235 ext_enable_name = ''
1236 for extension in self.registry.extensions:
1237 if extension.attrib['name'] == ext:
1238 ext_name_define = extension[0][1].get('name')
1239 ext_enable_name = ext_name_define.lower()
1240 ext_enable_name = re.sub('_extension_name', '', ext_enable_name)
1241 break
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001242 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 -06001243 lines.insert(0, ext_test)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001244 if lines:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001245 func_sig = self.getCmdDef(command) + ' {\n'
1246 func_sig = func_sig.split('VKAPI_CALL vk')[1]
1247 cmdDef = 'bool StatelessValidation::PreCallValidate' + func_sig
Mark Lobodzinskid4950072017-08-01 13:02:20 -06001248 cmdDef += '%sbool skip = false;\n' % indent
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001249 # Insert call to custom-written function if present
1250 if command.name in self.functions_with_manual_checks:
1251 # Generate parameter list for manual fcn and down-chain calls
1252 params_text = ''
1253 for param in command.params:
1254 params_text += '%s, ' % param.name
1255 params_text = params_text[:-2] + ');\n'
1256 cmdDef += ' skip |= manual_PreCallValidate'+ command.name[2:] + '(' + params_text
Mark Lobodzinski85672672016-10-13 08:36:42 -06001257 for line in lines:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001258 if type(line) is list:
1259 for sub in line:
1260 cmdDef += indent + sub
1261 else:
1262 cmdDef += indent + line
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001263 cmdDef += '%sreturn skip;\n' % indent
Mark Lobodzinski85672672016-10-13 08:36:42 -06001264 cmdDef += '}\n'
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001265 self.validation.append(cmdDef)