blob: 4bc12b1355b0867ca3accb3b1443e22c0f332a74 [file] [log] [blame]
Mark Lobodzinski85672672016-10-13 08:36:42 -06001#!/usr/bin/python3 -i
2#
Shahbaz Youssefi23aee922019-01-11 14:04:49 -05003# Copyright (c) 2015-2019 The Khronos Group Inc.
4# Copyright (c) 2015-2019 Valve Corporation
5# Copyright (c) 2015-2019 LunarG, Inc.
6# Copyright (c) 2015-2019 Google Inc.
Mark Lobodzinski85672672016-10-13 08:36:42 -06007#
8# Licensed under the Apache License, Version 2.0 (the "License");
9# you may not use this file except in compliance with the License.
10# You may obtain a copy of the License at
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS,
16# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17# See the License for the specific language governing permissions and
18# limitations under the License.
19#
20# Author: Dustin Graves <dustin@lunarg.com>
Mark Lobodzinski26112592017-05-30 12:02:17 -060021# Author: Mark Lobodzinski <mark@lunarg.com>
Dave Houlton413a6782018-05-22 13:01:54 -060022# Author: Dave Houlton <daveh@lunarg.com>
Mark Lobodzinski85672672016-10-13 08:36:42 -060023
Dave Houlton413a6782018-05-22 13:01:54 -060024import os,re,sys,string,json
Mark Lobodzinski85672672016-10-13 08:36:42 -060025import xml.etree.ElementTree as etree
26from generator import *
27from collections import namedtuple
Mark Lobodzinski62f71562017-10-24 13:41:18 -060028from common_codegen import *
Mark Lobodzinski06954ea2017-06-21 12:21:45 -060029
Jamie Madill8d4cda22017-11-08 13:40:09 -050030# This is a workaround to use a Python 2.7 and 3.x compatible syntax.
31from io import open
Mark Lobodzinski85672672016-10-13 08:36:42 -060032
Mark Lobodzinskid4950072017-08-01 13:02:20 -060033# ParameterValidationGeneratorOptions - subclass of GeneratorOptions.
Mark Lobodzinski85672672016-10-13 08:36:42 -060034#
Mark Lobodzinskid4950072017-08-01 13:02:20 -060035# Adds options used by ParameterValidationOutputGenerator object during Parameter validation layer generation.
Mark Lobodzinski85672672016-10-13 08:36:42 -060036#
37# Additional members
38# prefixText - list of strings to prefix generated header with
39# (usually a copyright statement + calling convention macros).
40# protectFile - True if multiple inclusion protection should be
41# generated (based on the filename) around the entire header.
42# protectFeature - True if #ifndef..#endif protection should be
43# generated around a feature interface in the header file.
44# genFuncPointers - True if function pointer typedefs should be
45# generated
46# protectProto - If conditional protection should be generated
47# around prototype declarations, set to either '#ifdef'
48# to require opt-in (#ifdef protectProtoStr) or '#ifndef'
49# to require opt-out (#ifndef protectProtoStr). Otherwise
50# set to None.
51# protectProtoStr - #ifdef/#ifndef symbol to use around prototype
52# declarations, if protectProto is set
53# apicall - string to use for the function declaration prefix,
54# such as APICALL on Windows.
55# apientry - string to use for the calling convention macro,
56# in typedefs, such as APIENTRY.
57# apientryp - string to use for the calling convention macro
58# in function pointer typedefs, such as APIENTRYP.
59# indentFuncProto - True if prototype declarations should put each
60# parameter on a separate line
61# indentFuncPointer - True if typedefed function pointers should put each
62# parameter on a separate line
63# alignFuncParam - if nonzero and parameters are being put on a
64# separate line, align parameter names at the specified column
Mark Lobodzinskid4950072017-08-01 13:02:20 -060065class ParameterValidationGeneratorOptions(GeneratorOptions):
Mark Lobodzinski85672672016-10-13 08:36:42 -060066 def __init__(self,
Mike Schuchardt21638df2019-03-16 10:52:02 -070067 conventions = None,
Mark Lobodzinski85672672016-10-13 08:36:42 -060068 filename = None,
69 directory = '.',
70 apiname = None,
71 profile = None,
72 versions = '.*',
73 emitversions = '.*',
74 defaultExtensions = None,
75 addExtensions = None,
76 removeExtensions = None,
Mark Lobodzinski62f71562017-10-24 13:41:18 -060077 emitExtensions = None,
Mark Lobodzinski85672672016-10-13 08:36:42 -060078 sortProcedure = regSortFeatures,
79 prefixText = "",
Mark Lobodzinski85672672016-10-13 08:36:42 -060080 apicall = '',
81 apientry = '',
82 apientryp = '',
83 indentFuncProto = True,
84 indentFuncPointer = False,
Mark Lobodzinski62f71562017-10-24 13:41:18 -060085 alignFuncParam = 0,
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -060086 expandEnumerants = True,
87 valid_usage_path = ''):
Mike Schuchardt21638df2019-03-16 10:52:02 -070088 GeneratorOptions.__init__(self, conventions, filename, directory, apiname, profile,
Mark Lobodzinski85672672016-10-13 08:36:42 -060089 versions, emitversions, defaultExtensions,
Mark Lobodzinski62f71562017-10-24 13:41:18 -060090 addExtensions, removeExtensions, emitExtensions, sortProcedure)
Mark Lobodzinski85672672016-10-13 08:36:42 -060091 self.prefixText = prefixText
Mark Lobodzinski85672672016-10-13 08:36:42 -060092 self.apicall = apicall
93 self.apientry = apientry
94 self.apientryp = apientryp
95 self.indentFuncProto = indentFuncProto
96 self.indentFuncPointer = indentFuncPointer
97 self.alignFuncParam = alignFuncParam
Mark Lobodzinski62f71562017-10-24 13:41:18 -060098 self.expandEnumerants = expandEnumerants
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -060099 self.valid_usage_path = valid_usage_path
Mark Lobodzinski85672672016-10-13 08:36:42 -0600100
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600101# ParameterValidationOutputGenerator - subclass of OutputGenerator.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600102# Generates param checker layer code.
103#
104# ---- methods ----
105# ParamCheckerOutputGenerator(errFile, warnFile, diagFile) - args as for
106# OutputGenerator. Defines additional internal state.
107# ---- methods overriding base class ----
108# beginFile(genOpts)
109# endFile()
110# beginFeature(interface, emit)
111# endFeature()
112# genType(typeinfo,name)
113# genStruct(typeinfo,name)
114# genGroup(groupinfo,name)
115# genEnum(enuminfo, name)
116# genCmd(cmdinfo)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600117class ParameterValidationOutputGenerator(OutputGenerator):
118 """Generate Parameter Validation code based on XML element attributes"""
Mark Lobodzinski85672672016-10-13 08:36:42 -0600119 # This is an ordered list of sections in the header file.
120 ALL_SECTIONS = ['command']
121 def __init__(self,
122 errFile = sys.stderr,
123 warnFile = sys.stderr,
124 diagFile = sys.stdout):
125 OutputGenerator.__init__(self, errFile, warnFile, diagFile)
126 self.INDENT_SPACES = 4
Mark Lobodzinskib6b8bbd2017-02-08 14:37:15 -0700127 self.declarations = []
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700128
129 inline_custom_source_preamble = """
130"""
131
Jeff Bolz7e7e6e02019-01-11 22:53:41 -0600132 # These functions have additional, custom-written checks in the utils cpp file. CodeGen will automatically add a call
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700133 # to those functions of the form 'bool manual_PreCallValidateAPIName', where the 'vk' is dropped.
134 # see 'manual_PreCallValidateCreateGraphicsPipelines' as an example.
135 self.functions_with_manual_checks = [
136 'vkCreateInstance',
137 'vkCreateDevice',
138 'vkCreateQueryPool'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700139 'vkCreateRenderPass',
140 'vkCreateRenderPass2KHR',
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700141 'vkCreateBuffer',
142 'vkCreateImage',
143 'vkCreateImageView',
144 'vkCreateGraphicsPipelines',
145 'vkCreateComputePipelines',
Peter Chen85366392019-05-14 15:20:11 -0400146 "vkCreateRayTracingPipelinesNV",
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700147 'vkCreateSampler',
148 'vkCreateDescriptorSetLayout',
149 'vkFreeDescriptorSets',
150 'vkUpdateDescriptorSets',
151 'vkCreateRenderPass',
152 'vkCreateRenderPass2KHR',
153 'vkBeginCommandBuffer',
154 'vkCmdSetViewport',
155 'vkCmdSetScissor',
156 'vkCmdSetLineWidth',
157 'vkCmdDraw',
158 'vkCmdDrawIndirect',
159 'vkCmdDrawIndexedIndirect',
Mark Lobodzinskif77a4ac2019-06-27 15:30:51 -0600160 'vkCmdClearAttachments',
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700161 'vkCmdCopyImage',
162 'vkCmdBlitImage',
163 'vkCmdCopyBufferToImage',
164 'vkCmdCopyImageToBuffer',
165 'vkCmdUpdateBuffer',
166 'vkCmdFillBuffer',
167 'vkCreateSwapchainKHR',
168 'vkQueuePresentKHR',
169 'vkCreateDescriptorPool',
170 'vkCmdDispatch',
171 'vkCmdDispatchIndirect',
172 'vkCmdDispatchBaseKHR',
173 'vkCmdSetExclusiveScissorNV',
174 'vkCmdSetViewportShadingRatePaletteNV',
175 'vkCmdSetCoarseSampleOrderNV',
176 'vkCmdDrawMeshTasksNV',
177 'vkCmdDrawMeshTasksIndirectNV',
178 'vkCmdDrawMeshTasksIndirectCountNV',
Jeff Bolz7e7e6e02019-01-11 22:53:41 -0600179 'vkAllocateMemory',
Ricardo Garciaa4935972019-02-21 17:43:18 +0100180 'vkCreateAccelerationStructureNV',
Jason Macnak5c954952019-07-09 15:46:12 -0700181 'vkGetAccelerationStructureHandleNV',
182 'vkCmdBuildAccelerationStructureNV',
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700183 ]
184
Mark Lobodzinski85672672016-10-13 08:36:42 -0600185 # Commands to ignore
186 self.blacklist = [
187 'vkGetInstanceProcAddr',
188 'vkGetDeviceProcAddr',
Mark Young6ba8abe2017-11-09 10:37:04 -0700189 'vkEnumerateInstanceVersion',
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600190 'vkEnumerateInstanceLayerProperties',
191 'vkEnumerateInstanceExtensionProperties',
192 'vkEnumerateDeviceLayerProperties',
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600193 'vkEnumerateDeviceExtensionProperties',
Mike Schuchardt21638df2019-03-16 10:52:02 -0700194 'vkGetDeviceGroupSurfacePresentModes2EXT'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600195 ]
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700196
Dustin Gravesce68f082017-03-30 15:42:16 -0600197 # Structure fields to ignore
198 self.structMemberBlacklist = { 'VkWriteDescriptorSet' : ['dstSet'] }
Mark Lobodzinski85672672016-10-13 08:36:42 -0600199 # Validation conditions for some special case struct members that are conditionally validated
200 self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
201 # Header version
202 self.headerVersion = None
203 # Internal state - accumulators for different inner block text
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600204 self.validation = [] # Text comprising the main per-api parameter validation routines
Mark Lobodzinski85672672016-10-13 08:36:42 -0600205 self.stypes = [] # Values from the VkStructureType enumeration
206 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
207 self.handleTypes = set() # Set of handle type names
208 self.commands = [] # List of CommandData records for all Vulkan commands
209 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
210 self.validatedStructs = dict() # Map of structs type names to generated validation code for that struct type
211 self.enumRanges = dict() # Map of enum name to BEGIN/END range values
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600212 self.enumValueLists = '' # String containing enumerated type map definitions
Mark Lobodzinski85672672016-10-13 08:36:42 -0600213 self.flags = set() # Map of flags typenames
214 self.flagBits = dict() # Map of flag bits typename to list of values
Chris Forbes78ea32d2016-11-28 11:14:17 +1300215 self.newFlags = set() # Map of flags typenames /defined in the current feature/
Mike Schuchardtafd00482017-08-24 15:15:02 -0600216 self.required_extensions = dict() # Dictionary of required extensions for each item in the current extension
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600217 self.extension_type = '' # Type of active feature (extension), device or instance
218 self.extension_names = dict() # Dictionary of extension names to extension name defines
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600219 self.structextends_list = [] # List of extensions which extend another struct
220 self.struct_feature_protect = dict() # Dictionary of structnames and FeatureExtraProtect strings
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600221 self.valid_vuids = set() # Set of all valid VUIDs
Dave Houlton413a6782018-05-22 13:01:54 -0600222 self.vuid_dict = dict() # VUID dictionary (from JSON)
223 self.alias_dict = dict() # Dict of cmd|struct aliases
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700224 self.header_file = False # Header file generation flag
225 self.source_file = False # Source file generation flag
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600226 self.returnedonly_structs = []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600227 # Named tuples to store struct and command data
Mark Lobodzinski85672672016-10-13 08:36:42 -0600228 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600229 'isconst', 'isoptional', 'iscount', 'noautovalidity',
230 'len', 'extstructs', 'condition', 'cdecl'])
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600231 self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl', 'extension_type', 'result'])
Mark Lobodzinski85672672016-10-13 08:36:42 -0600232 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600233
Mark Lobodzinski85672672016-10-13 08:36:42 -0600234 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600235 # Generate Copyright comment block for file
236 def GenerateCopyright(self):
237 copyright = '/* *** THIS FILE IS GENERATED - DO NOT EDIT! ***\n'
238 copyright += ' * See parameter_validation_generator.py for modifications\n'
239 copyright += ' *\n'
Mike Schuchardt840b5042019-07-11 08:11:47 -0700240 copyright += ' * Copyright (c) 2015-2019 The Khronos Group Inc.\n'
241 copyright += ' * Copyright (c) 2015-2019 LunarG, Inc.\n'
242 copyright += ' * Copyright (C) 2015-2019 Google Inc.\n'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600243 copyright += ' *\n'
244 copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
245 copyright += ' * you may not use this file except in compliance with the License.\n'
246 copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n'
247 copyright += ' * You may obtain a copy of the License at\n'
248 copyright += ' *\n'
249 copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n'
250 copyright += ' *\n'
251 copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
252 copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
253 copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
254 copyright += ' * See the License for the specific language governing permissions and\n'
255 copyright += ' * limitations under the License.\n'
256 copyright += ' *\n'
257 copyright += ' * Author: Mark Lobodzinski <mark@LunarG.com>\n'
Dave Houlton413a6782018-05-22 13:01:54 -0600258 copyright += ' * Author: Dave Houlton <daveh@LunarG.com>\n'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600259 copyright += ' */\n\n'
260 return copyright
261 #
262 # Increases the global indent variable
Mark Lobodzinski85672672016-10-13 08:36:42 -0600263 def incIndent(self, indent):
264 inc = ' ' * self.INDENT_SPACES
265 if indent:
266 return indent + inc
267 return inc
268 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600269 # Decreases the global indent variable
Mark Lobodzinski85672672016-10-13 08:36:42 -0600270 def decIndent(self, indent):
271 if indent and (len(indent) > self.INDENT_SPACES):
272 return indent[:-self.INDENT_SPACES]
273 return ''
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600274 #
Dave Houlton413a6782018-05-22 13:01:54 -0600275 # Walk the JSON-derived dict and find all "vuid" key values
276 def ExtractVUIDs(self, d):
277 if hasattr(d, 'items'):
278 for k, v in d.items():
279 if k == "vuid":
280 yield v
281 elif isinstance(v, dict):
282 for s in self.ExtractVUIDs(v):
283 yield s
284 elif isinstance (v, list):
285 for l in v:
286 for s in self.ExtractVUIDs(l):
287 yield s
Mark Lobodzinski85672672016-10-13 08:36:42 -0600288 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600289 # Called at file creation time
Mark Lobodzinski85672672016-10-13 08:36:42 -0600290 def beginFile(self, genOpts):
291 OutputGenerator.beginFile(self, genOpts)
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700292 self.header_file = (genOpts.filename == 'parameter_validation.h')
293 self.source_file = (genOpts.filename == 'parameter_validation.cpp')
294
295 if not self.header_file and not self.source_file:
296 print("Error: Output Filenames have changed, update generator source.\n")
297 sys.exit(1)
298
299 if self.source_file or self.header_file:
300 # Output Copyright text
301 s = self.GenerateCopyright()
302 write(s, file=self.outFile)
303
304 if self.header_file:
305 return
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -0600306
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700307 # Build map of structure type names to VkStructureType enum values
308 # Find all types of category "struct"
309 for struct in self.registry.tree.iterfind('types/type[@category="struct"]'):
310 # Check if struct has member named "sType" of type "VkStructureType" which has values defined
311 stype = struct.find('member[name="sType"][type="VkStructureType"][@values]')
Shahbaz Youssefi23aee922019-01-11 14:04:49 -0500312 if stype is not None:
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700313 # Store VkStructureType value for this type
314 self.structTypes[struct.get('name')] = stype.get('values')
315
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -0600316 self.valid_usage_path = genOpts.valid_usage_path
317 vu_json_filename = os.path.join(self.valid_usage_path + os.sep, 'validusage.json')
318 if os.path.isfile(vu_json_filename):
319 json_file = open(vu_json_filename, 'r')
320 self.vuid_dict = json.load(json_file)
321 json_file.close()
322 if len(self.vuid_dict) == 0:
323 print("Error: Could not find, or error loading %s/validusage.json\n", vu_json_filename)
324 sys.exit(1)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600325 #
Dave Houlton413a6782018-05-22 13:01:54 -0600326 # Build a set of all vuid text strings found in validusage.json
327 for json_vuid_string in self.ExtractVUIDs(self.vuid_dict):
328 self.valid_vuids.add(json_vuid_string)
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600329 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600330 # Headers
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700331 write('#include "chassis.h"', file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600332 self.newline()
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700333 write('#include "stateless_validation.h"', file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600334 self.newline()
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600335 #
336 # Called at end-time for final content output
Mark Lobodzinski85672672016-10-13 08:36:42 -0600337 def endFile(self):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700338 if self.source_file:
339 # C-specific
340 self.newline()
341 write(self.enumValueLists, file=self.outFile)
342 self.newline()
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600343
Locke6b6b7382019-04-16 15:08:49 -0600344 pnext_handler = 'bool StatelessValidation::ValidatePnextStructContents(const char *api_name, const ParameterName &parameter_name, const VkBaseOutStructure* header) {\n'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700345 pnext_handler += ' bool skip = false;\n'
346 pnext_handler += ' switch(header->sType) {\n'
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600347
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700348 # Do some processing here to extract data from validatedstructs...
349 for item in self.structextends_list:
350 postProcSpec = {}
351 postProcSpec['ppp'] = '' if not item else '{postProcPrefix}'
352 postProcSpec['pps'] = '' if not item else '{postProcSuffix}'
353 postProcSpec['ppi'] = '' if not item else '{postProcInsert}'
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600354
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700355 pnext_case = '\n'
356 protect = ''
357 # Guard struct cases with feature ifdefs, if necessary
358 if item in self.struct_feature_protect.keys():
359 protect = self.struct_feature_protect[item]
360 pnext_case += '#ifdef %s\n' % protect
361 pnext_case += ' // Validation code for %s structure members\n' % item
362 pnext_case += ' case %s: {\n' % self.structTypes[item]
363 pnext_case += ' %s *structure = (%s *) header;\n' % (item, item)
364 expr = self.expandStructCode(item, item, 'structure->', '', ' ', [], postProcSpec)
365 struct_validation_source = self.ScrubStructCode(expr)
366 pnext_case += '%s' % struct_validation_source
367 pnext_case += ' } break;\n'
Raul Tambre7b300182019-05-04 11:25:14 +0300368 if protect:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700369 pnext_case += '#endif // %s\n' % protect
370 # Skip functions containing no validation
Raul Tambre7b300182019-05-04 11:25:14 +0300371 if struct_validation_source:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700372 pnext_handler += pnext_case;
373 pnext_handler += ' default:\n'
374 pnext_handler += ' skip = false;\n'
375 pnext_handler += ' }\n'
376 pnext_handler += ' return skip;\n'
377 pnext_handler += '}\n'
378 write(pnext_handler, file=self.outFile)
379 self.newline()
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600380
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700381 ext_template = 'bool StatelessValidation::OutputExtensionError(const std::string &api_name, const std::string &extension_name) {\n'
382 ext_template += ' return log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,\n'
383 ext_template += ' kVUID_PVError_ExtensionNotEnabled, "Attemped to call %s() but its required extension %s has not been enabled\\n",\n'
384 ext_template += ' api_name.c_str(), extension_name.c_str());\n'
385 ext_template += '}\n'
386 write(ext_template, file=self.outFile)
387 self.newline()
388 commands_text = '\n'.join(self.validation)
389 write(commands_text, file=self.outFile)
390 self.newline()
391 if self.header_file:
392 # Output declarations and record intercepted procedures
393 write('\n'.join(self.declarations), file=self.outFile)
394 # Finish processing in superclass
395 OutputGenerator.endFile(self)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600396 #
397 # Processing at beginning of each feature or extension
Mark Lobodzinski85672672016-10-13 08:36:42 -0600398 def beginFeature(self, interface, emit):
399 # Start processing in superclass
400 OutputGenerator.beginFeature(self, interface, emit)
401 # C-specific
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600402 # Accumulate includes, defines, types, enums, function pointer typedefs, end function prototypes separately for this
403 # feature. They're only printed in endFeature().
Mark Lobodzinski85672672016-10-13 08:36:42 -0600404 self.headerVersion = None
Mark Lobodzinski85672672016-10-13 08:36:42 -0600405 self.stypes = []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600406 self.commands = []
407 self.structMembers = []
Chris Forbes78ea32d2016-11-28 11:14:17 +1300408 self.newFlags = set()
Mark Lobodzinski62f71562017-10-24 13:41:18 -0600409 self.featureExtraProtect = GetFeatureProtect(interface)
Mike Schuchardtafd00482017-08-24 15:15:02 -0600410 # Get base list of extension dependencies for all items in this extension
411 base_required_extensions = []
Mark Lobodzinski31964ca2017-09-18 14:15:09 -0600412 if "VK_VERSION_1" not in self.featureName:
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600413 # Save Name Define to get correct enable name later
414 nameElem = interface[0][1]
415 name = nameElem.get('name')
416 self.extension_names[self.featureName] = name
417 # This extension is the first dependency for this command
Mike Schuchardtafd00482017-08-24 15:15:02 -0600418 base_required_extensions.append(self.featureName)
419 # Add any defined extension dependencies to the base dependency list for this extension
420 requires = interface.get('requires')
421 if requires is not None:
422 base_required_extensions.extend(requires.split(','))
Mike Schuchardtafd00482017-08-24 15:15:02 -0600423 # Build dictionary of extension dependencies for each item in this extension
424 self.required_extensions = dict()
425 for require_element in interface.findall('require'):
426 # Copy base extension dependency list
427 required_extensions = list(base_required_extensions)
428 # Add any additional extension dependencies specified in this require block
429 additional_extensions = require_element.get('extension')
430 if additional_extensions:
431 required_extensions.extend(additional_extensions.split(','))
432 # Save full extension list for all named items
433 for element in require_element.findall('*[@name]'):
434 self.required_extensions[element.get('name')] = required_extensions
435
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600436 # And note if this is an Instance or Device extension
437 self.extension_type = interface.get('type')
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600438 #
439 # Called at the end of each extension (feature)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600440 def endFeature(self):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700441 if self.header_file:
442 return
Mark Lobodzinski85672672016-10-13 08:36:42 -0600443 # C-specific
444 # Actually write the interface to the output file.
445 if (self.emit):
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600446 # 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 -0600447 # or move it below the 'for section...' loop.
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600448 ifdef = ''
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100449 if (self.featureExtraProtect is not None):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600450 ifdef = '#ifdef %s\n' % self.featureExtraProtect
451 self.validation.append(ifdef)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600452 # Generate the struct member checking code from the captured data
453 self.processStructMemberData()
454 # Generate the command parameter checking code from the captured data
455 self.processCmdData()
456 # Write the declaration for the HeaderVersion
457 if self.headerVersion:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700458 write('const uint32_t GeneratedVulkanHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600459 self.newline()
460 # Write the declarations for the VkFlags values combining all flag bits
Chris Forbes78ea32d2016-11-28 11:14:17 +1300461 for flag in sorted(self.newFlags):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600462 flagBits = flag.replace('Flags', 'FlagBits')
463 if flagBits in self.flagBits:
464 bits = self.flagBits[flagBits]
465 decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
466 for bit in bits[1:]:
467 decl += '|' + bit
468 decl += ';'
469 write(decl, file=self.outFile)
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600470 endif = '\n'
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100471 if (self.featureExtraProtect is not None):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600472 endif = '#endif // %s\n' % self.featureExtraProtect
473 self.validation.append(endif)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600474 # Finish processing in superclass
475 OutputGenerator.endFeature(self)
476 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600477 # Type generation
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700478 def genType(self, typeinfo, name, alias):
Dave Houlton413a6782018-05-22 13:01:54 -0600479 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100480 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600481 self.alias_dict[name]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700482 OutputGenerator.genType(self, typeinfo, name, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600483 typeElem = typeinfo.elem
Mark Lobodzinski87017df2018-05-30 11:29:24 -0600484 # 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 -0600485 category = typeElem.get('category')
486 if (category == 'struct' or category == 'union'):
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700487 self.genStruct(typeinfo, name, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600488 elif (category == 'handle'):
489 self.handleTypes.add(name)
490 elif (category == 'bitmask'):
491 self.flags.add(name)
Chris Forbes78ea32d2016-11-28 11:14:17 +1300492 self.newFlags.add(name)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600493 elif (category == 'define'):
494 if name == 'VK_HEADER_VERSION':
495 nameElem = typeElem.find('name')
496 self.headerVersion = noneStr(nameElem.tail).strip()
497 #
498 # Struct parameter check generation.
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600499 # This is a special case of the <type> tag where the contents are interpreted as a set of <member> tags instead of freeform C
500 # type declarations. The <member> tags are just like <param> tags - they are a declaration of a struct or union member.
501 # Only simple member declarations are supported (no nested structs etc.)
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700502 def genStruct(self, typeinfo, typeName, alias):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700503 if not self.source_file:
504 return
Dave Houlton413a6782018-05-22 13:01:54 -0600505 # alias has already been recorded in genType, above
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700506 OutputGenerator.genStruct(self, typeinfo, typeName, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600507 conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
508 members = typeinfo.elem.findall('.//member')
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600509 if self.featureExtraProtect is not None:
510 self.struct_feature_protect[typeName] = self.featureExtraProtect
Mark Lobodzinski85672672016-10-13 08:36:42 -0600511 #
512 # Iterate over members once to get length parameters for arrays
513 lens = set()
514 for member in members:
515 len = self.getLen(member)
516 if len:
517 lens.add(len)
518 #
519 # Generate member info
520 membersInfo = []
521 for member in members:
522 # Get the member's type and name
523 info = self.getTypeNameTuple(member)
524 type = info[0]
525 name = info[1]
526 stypeValue = ''
527 cdecl = self.makeCParamDecl(member, 0)
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700528
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600529 # Store pointer/array/string info -- Check for parameter name in lens set
Mark Lobodzinski85672672016-10-13 08:36:42 -0600530 iscount = False
531 if name in lens:
532 iscount = True
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600533 # The pNext members are not tagged as optional, but are treated as optional for parameter NULL checks. Static array
534 # members are also treated as optional to skip NULL pointer validation, as they won't be NULL.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600535 isstaticarray = self.paramIsStaticArray(member)
536 isoptional = False
537 if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
538 isoptional = True
Dustin Gravesce68f082017-03-30 15:42:16 -0600539 # Determine if value should be ignored by code generation.
540 noautovalidity = False
541 if (member.attrib.get('noautovalidity') is not None) or ((typeName in self.structMemberBlacklist) and (name in self.structMemberBlacklist[typeName])):
542 noautovalidity = True
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600543 structextends = False
Mark Lobodzinski85672672016-10-13 08:36:42 -0600544 membersInfo.append(self.CommandParam(type=type, name=name,
545 ispointer=self.paramIsPointer(member),
546 isstaticarray=isstaticarray,
547 isbool=True if type == 'VkBool32' else False,
548 israngedenum=True if type in self.enumRanges else False,
549 isconst=True if 'const' in cdecl else False,
550 isoptional=isoptional,
551 iscount=iscount,
Dustin Gravesce68f082017-03-30 15:42:16 -0600552 noautovalidity=noautovalidity,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600553 len=self.getLen(member),
Mike Schuchardta40d0b02017-07-23 12:47:47 -0600554 extstructs=self.registry.validextensionstructs[typeName] if name == 'pNext' else None,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600555 condition=conditions[name] if conditions and name in conditions else None,
556 cdecl=cdecl))
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600557 # If this struct extends another, keep its name in list for further processing
558 if typeinfo.elem.attrib.get('structextends') is not None:
559 self.structextends_list.append(typeName)
560 # Returnedonly structs should have most of their members ignored -- on entry, we only care about validating the sType and
561 # pNext members. Everything else will be overwritten by the callee.
562 if typeinfo.elem.attrib.get('returnedonly') is not None:
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600563 self.returnedonly_structs.append(typeName)
564 membersInfo = [m for m in membersInfo if m.name in ('sType', 'pNext')]
Mark Lobodzinski85672672016-10-13 08:36:42 -0600565 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
566 #
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600567 # Capture group (e.g. C "enum" type) info to be used for param check code generation.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600568 # These are concatenated together with other types.
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700569 def genGroup(self, groupinfo, groupName, alias):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700570 if not self.source_file:
571 return
Dave Houlton413a6782018-05-22 13:01:54 -0600572 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100573 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600574 self.alias_dict[groupName]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700575 OutputGenerator.genGroup(self, groupinfo, groupName, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600576 groupElem = groupinfo.elem
Mark Lobodzinski85672672016-10-13 08:36:42 -0600577 # Store the sType values
578 if groupName == 'VkStructureType':
579 for elem in groupElem.findall('enum'):
580 self.stypes.append(elem.get('name'))
581 elif 'FlagBits' in groupName:
582 bits = []
583 for elem in groupElem.findall('enum'):
Shannon McPherson533a66c2018-08-21 12:09:25 -0600584 if elem.get('supported') != 'disabled':
585 bits.append(elem.get('name'))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600586 if bits:
587 self.flagBits[groupName] = bits
588 else:
589 # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
590 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
591 expandPrefix = expandName
592 expandSuffix = ''
593 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
594 if expandSuffixMatch:
595 expandSuffix = '_' + expandSuffixMatch.group()
596 # Strip off the suffix from the prefix
597 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
598 isEnum = ('FLAG_BITS' not in expandPrefix)
599 if isEnum:
600 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600601 # Create definition for a list containing valid enum values for this enumerated type
Mike Schuchardt21638df2019-03-16 10:52:02 -0700602 if self.featureExtraProtect is not None:
603 enum_entry = '\n#ifdef %s\n' % self.featureExtraProtect
604 else:
605 enum_entry = ''
606 enum_entry += 'const std::vector<%s> All%sEnums = {' % (groupName, groupName)
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600607 for enum in groupElem:
608 name = enum.get('name')
Mark Lobodzinski117d88f2017-07-27 12:09:08 -0600609 if name is not None and enum.get('supported') != 'disabled':
610 enum_entry += '%s, ' % name
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600611 enum_entry += '};\n'
Mike Schuchardt21638df2019-03-16 10:52:02 -0700612 if self.featureExtraProtect is not None:
613 enum_entry += '#endif // %s' % self.featureExtraProtect
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600614 self.enumValueLists += enum_entry
Mark Lobodzinski85672672016-10-13 08:36:42 -0600615 #
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600616 # Capture command parameter info to be used for param check code generation.
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700617 def genCmd(self, cmdinfo, name, alias):
Dave Houlton413a6782018-05-22 13:01:54 -0600618 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100619 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600620 self.alias_dict[name]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700621 OutputGenerator.genCmd(self, cmdinfo, name, alias)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600622 decls = self.makeCDecls(cmdinfo.elem)
623 typedef = decls[1]
624 typedef = typedef.split(')',1)[1]
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700625 if self.header_file:
626 if name not in self.blacklist:
627 if (self.featureExtraProtect is not None):
628 self.declarations += [ '#ifdef %s' % self.featureExtraProtect ]
629 # Strip off 'vk' from API name
630 self.declarations += [ '%s%s' % ('bool PreCallValidate', decls[0].split("VKAPI_CALL vk")[1])]
631 if (self.featureExtraProtect is not None):
632 self.declarations += [ '#endif' ]
633 if self.source_file:
634 if name not in self.blacklist:
635 params = cmdinfo.elem.findall('param')
636 # Get list of array lengths
637 lens = set()
638 for param in params:
639 len = self.getLen(param)
640 if len:
641 lens.add(len)
642 # Get param info
643 paramsInfo = []
644 for param in params:
645 paramInfo = self.getTypeNameTuple(param)
646 cdecl = self.makeCParamDecl(param, 0)
647 # Check for parameter name in lens set
648 iscount = False
649 if paramInfo[1] in lens:
650 iscount = True
651 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
652 ispointer=self.paramIsPointer(param),
653 isstaticarray=self.paramIsStaticArray(param),
654 isbool=True if paramInfo[0] == 'VkBool32' else False,
655 israngedenum=True if paramInfo[0] in self.enumRanges else False,
656 isconst=True if 'const' in cdecl else False,
657 isoptional=self.paramIsOptional(param),
658 iscount=iscount,
659 noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
660 len=self.getLen(param),
661 extstructs=None,
662 condition=None,
663 cdecl=cdecl))
664 # Save return value information, if any
665 result_type = ''
666 resultinfo = cmdinfo.elem.find('proto/type')
667 if (resultinfo is not None and resultinfo.text != 'void'):
668 result_type = resultinfo.text
669 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 -0600670 #
671 # Check if the parameter passed in is a pointer
672 def paramIsPointer(self, param):
673 ispointer = 0
674 paramtype = param.find('type')
675 if (paramtype.tail is not None) and ('*' in paramtype.tail):
676 ispointer = paramtype.tail.count('*')
677 elif paramtype.text[:4] == 'PFN_':
678 # Treat function pointer typedefs as a pointer to a single value
679 ispointer = 1
680 return ispointer
681 #
682 # Check if the parameter passed in is a static array
683 def paramIsStaticArray(self, param):
684 isstaticarray = 0
685 paramname = param.find('name')
686 if (paramname.tail is not None) and ('[' in paramname.tail):
687 isstaticarray = paramname.tail.count('[')
688 return isstaticarray
689 #
690 # Check if the parameter passed in is optional
691 # Returns a list of Boolean values for comma separated len attributes (len='false,true')
692 def paramIsOptional(self, param):
693 # See if the handle is optional
694 isoptional = False
695 # Simple, if it's optional, return true
696 optString = param.attrib.get('optional')
697 if optString:
698 if optString == 'true':
699 isoptional = True
700 elif ',' in optString:
701 opts = []
702 for opt in optString.split(','):
703 val = opt.strip()
704 if val == 'true':
705 opts.append(True)
706 elif val == 'false':
707 opts.append(False)
708 else:
709 print('Unrecognized len attribute value',val)
710 isoptional = opts
711 return isoptional
712 #
713 # Check if the handle passed in is optional
714 # Uses the same logic as ValidityOutputGenerator.isHandleOptional
715 def isHandleOptional(self, param, lenParam):
716 # Simple, if it's optional, return true
717 if param.isoptional:
718 return True
719 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
720 if param.noautovalidity:
721 return True
722 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
723 if lenParam and lenParam.isoptional:
724 return True
725 return False
726 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600727 # Retrieve the value of the len tag
728 def getLen(self, param):
729 result = None
730 len = param.attrib.get('len')
731 if len and len != 'null-terminated':
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600732 # For string arrays, 'len' can look like 'count,null-terminated', indicating that we have a null terminated array of
733 # 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 -0600734 if 'null-terminated' in len:
735 result = len.split(',')[0]
736 else:
737 result = len
738 result = str(result).replace('::', '->')
739 return result
740 #
741 # Retrieve the type and name for a parameter
742 def getTypeNameTuple(self, param):
743 type = ''
744 name = ''
745 for elem in param:
746 if elem.tag == 'type':
747 type = noneStr(elem.text)
748 elif elem.tag == 'name':
749 name = noneStr(elem.text)
750 return (type, name)
751 #
752 # Find a named parameter in a parameter list
753 def getParamByName(self, params, name):
754 for param in params:
755 if param.name == name:
756 return param
757 return None
758 #
759 # Extract length values from latexmath. Currently an inflexible solution that looks for specific
760 # patterns that are found in vk.xml. Will need to be updated when new patterns are introduced.
761 def parseLateXMath(self, source):
762 name = 'ERROR'
763 decoratedName = 'ERROR'
764 if 'mathit' in source:
Mark Lobodzinski36c33862017-02-13 10:15:53 -0700765 # Matches expressions similar to 'latexmath:[\lceil{\mathit{rasterizationSamples} \over 32}\rceil]'
766 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 -0600767 if not match or match.group(1) != match.group(4):
768 raise 'Unrecognized latexmath expression'
769 name = match.group(2)
770 decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
771 else:
Mark Lobodzinski36c33862017-02-13 10:15:53 -0700772 # Matches expressions similar to 'latexmath : [dataSize \over 4]'
Shannon McPhersonbd68df02018-10-29 15:04:41 -0600773 match = re.match(r'latexmath\s*\:\s*\[\s*(\\textrm\{)?(\w+)\}?\s*\\over\s*(\d+)\s*\]', source)
774 name = match.group(2)
775 decoratedName = '{}/{}'.format(*match.group(2, 3))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600776 return name, decoratedName
777 #
778 # Get the length paramater record for the specified parameter name
779 def getLenParam(self, params, name):
780 lenParam = None
781 if name:
782 if '->' in name:
783 # The count is obtained by dereferencing a member of a struct parameter
784 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600785 isstaticarray=None, isoptional=False, type=None, noautovalidity=False,
786 len=None, extstructs=None, condition=None, cdecl=None)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600787 elif 'latexmath' in name:
788 lenName, decoratedName = self.parseLateXMath(name)
789 lenParam = self.getParamByName(params, lenName)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600790 else:
791 lenParam = self.getParamByName(params, name)
792 return lenParam
793 #
794 # Convert a vulkan.h command declaration into a parameter_validation.h definition
795 def getCmdDef(self, cmd):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600796 # Strip the trailing ';' and split into individual lines
797 lines = cmd.cdecl[:-1].split('\n')
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600798 cmd_hdr = '\n'.join(lines)
799 return cmd_hdr
Mark Lobodzinski85672672016-10-13 08:36:42 -0600800 #
801 # Generate the code to check for a NULL dereference before calling the
802 # validation function
803 def genCheckedLengthCall(self, name, exprs):
804 count = name.count('->')
805 if count:
806 checkedExpr = []
807 localIndent = ''
808 elements = name.split('->')
809 # Open the if expression blocks
810 for i in range(0, count):
811 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
812 localIndent = self.incIndent(localIndent)
813 # Add the validation expression
814 for expr in exprs:
815 checkedExpr.append(localIndent + expr)
816 # Close the if blocks
817 for i in range(0, count):
818 localIndent = self.decIndent(localIndent)
819 checkedExpr.append(localIndent + '}\n')
820 return [checkedExpr]
821 # No if statements were required
822 return exprs
823 #
824 # Generate code to check for a specific condition before executing validation code
825 def genConditionalCall(self, prefix, condition, exprs):
826 checkedExpr = []
827 localIndent = ''
828 formattedCondition = condition.format(prefix)
829 checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
830 checkedExpr.append(localIndent + '{\n')
831 localIndent = self.incIndent(localIndent)
832 for expr in exprs:
833 checkedExpr.append(localIndent + expr)
834 localIndent = self.decIndent(localIndent)
835 checkedExpr.append(localIndent + '}\n')
836 return [checkedExpr]
837 #
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600838 # Get VUID identifier from implicit VUID tag
Dave Houlton413a6782018-05-22 13:01:54 -0600839 def GetVuid(self, name, suffix):
840 vuid_string = 'VUID-%s-%s' % (name, suffix)
841 vuid = "kVUIDUndefined"
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600842 if '->' in vuid_string:
Dave Houlton413a6782018-05-22 13:01:54 -0600843 return vuid
844 if vuid_string in self.valid_vuids:
845 vuid = "\"%s\"" % vuid_string
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600846 else:
Dave Houlton413a6782018-05-22 13:01:54 -0600847 if name in self.alias_dict:
848 alias_string = 'VUID-%s-%s' % (self.alias_dict[name], suffix)
849 if alias_string in self.valid_vuids:
Shannon McPherson23d97212019-02-18 13:39:42 -0700850 vuid = "\"%s\"" % alias_string
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600851 return vuid
852 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600853 # Generate the sType check string
Mark Lobodzinski9cf24dd2017-06-28 14:23:22 -0600854 def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600855 checkExpr = []
856 stype = self.structTypes[value.type]
Mark Lobodzinski59603552018-05-29 16:14:59 -0600857 vuid_name = struct_type_name if struct_type_name is not None else funcPrintName
858 stype_vuid = self.GetVuid(value.type, "sType-sType")
859 param_vuid = self.GetVuid(vuid_name, "%s-parameter" % value.name)
860
Mark Lobodzinski85672672016-10-13 08:36:42 -0600861 if lenValue:
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800862 count_required_vuid = self.GetVuid(vuid_name, "%s-arraylength" % lenValue.name)
863
Mark Lobodzinski85672672016-10-13 08:36:42 -0600864 # This is an array with a pointer to a count value
865 if lenValue.ispointer:
866 # 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 -0800867 checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {}, {});\n'.format(
868 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 -0600869 # This is an array with an integer count value
870 else:
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800871 checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {});\n'.format(
872 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 -0600873 # This is an individual struct
874 else:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700875 checkExpr.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {}, {}, {});\n'.format(
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700876 funcPrintName, valuePrintName, prefix, valueRequired, param_vuid, stype_vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600877 return checkExpr
878 #
879 # Generate the handle check string
880 def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
881 checkExpr = []
882 if lenValue:
883 if lenValue.ispointer:
884 # This is assumed to be an output array with a pointer to a count value
885 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
886 else:
887 # This is an array with an integer count value
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700888 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 -0600889 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
890 else:
891 # This is assumed to be an output handle pointer
892 raise('Unsupported parameter validation case: Output handles are not NULL checked')
893 return checkExpr
894 #
895 # Generate check string for an array of VkFlags values
896 def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
897 checkExpr = []
898 flagBitsName = value.type.replace('Flags', 'FlagBits')
899 if not flagBitsName in self.flagBits:
900 raise('Unsupported parameter validation case: array of reserved VkFlags')
901 else:
902 allFlags = 'All' + flagBitsName
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700903 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 -0600904 return checkExpr
905 #
906 # Generate pNext check string
Mark Lobodzinski3c828522017-06-26 13:05:57 -0600907 def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600908 checkExpr = []
909 # Generate an array of acceptable VkStructureType values for pNext
910 extStructCount = 0
911 extStructVar = 'NULL'
912 extStructNames = 'NULL'
Dave Houlton413a6782018-05-22 13:01:54 -0600913 vuid = self.GetVuid(struct_type_name, "pNext-pNext")
Mark Lobodzinski85672672016-10-13 08:36:42 -0600914 if value.extstructs:
Mike Schuchardtc73d07e2017-07-12 10:10:01 -0600915 extStructVar = 'allowed_structs_{}'.format(struct_type_name)
916 extStructCount = 'ARRAY_SIZE({})'.format(extStructVar)
Mike Schuchardta40d0b02017-07-23 12:47:47 -0600917 extStructNames = '"' + ', '.join(value.extstructs) + '"'
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700918 checkExpr.append('const VkStructureType {}[] = {{ {} }};\n'.format(extStructVar, ', '.join([self.structTypes[s] for s in value.extstructs])))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700919 checkExpr.append('skip |= validate_struct_pnext("{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedVulkanHeaderVersion, {});\n'.format(
Mark Lobodzinski3c828522017-06-26 13:05:57 -0600920 funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, vuid, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600921 return checkExpr
922 #
923 # Generate the pointer check string
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600924 def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600925 checkExpr = []
Mark Lobodzinskidead0b62017-06-28 13:22:03 -0600926 vuid_tag_name = struct_type_name if struct_type_name is not None else funcPrintName
Mark Lobodzinski85672672016-10-13 08:36:42 -0600927 if lenValue:
Dave Houlton413a6782018-05-22 13:01:54 -0600928 count_required_vuid = self.GetVuid(vuid_tag_name, "%s-arraylength" % (lenValue.name))
929 array_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600930 # This is an array with a pointer to a count value
931 if lenValue.ispointer:
932 # If count and array parameters are optional, there will be no validation
933 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
934 # 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 -0700935 checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600936 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 -0600937 # This is an array with an integer count value
938 else:
939 # If count and array parameters are optional, there will be no validation
940 if valueRequired == 'true' or lenValueRequired == 'true':
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600941 if value.type != 'char':
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700942 checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600943 funcPrintName, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
944 else:
945 # Arrays of strings receive special processing
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700946 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 -0600947 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 -0600948 if checkExpr:
949 if lenValue and ('->' in lenValue.name):
950 # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
951 checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
952 # This is an individual struct that is not allowed to be NULL
953 elif not value.isoptional:
954 # Function pointers need a reinterpret_cast to void*
Dave Houlton413a6782018-05-22 13:01:54 -0600955 ptr_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600956 if value.type[:4] == 'PFN_':
Dave Houlton413a6782018-05-22 13:01:54 -0600957 allocator_dict = {'pfnAllocation': '"VUID-VkAllocationCallbacks-pfnAllocation-00632"',
958 'pfnReallocation': '"VUID-VkAllocationCallbacks-pfnReallocation-00633"',
959 'pfnFree': '"VUID-VkAllocationCallbacks-pfnFree-00634"',
Mark Lobodzinski02fa1972017-06-28 14:46:14 -0600960 }
961 vuid = allocator_dict.get(value.name)
962 if vuid is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600963 ptr_required_vuid = vuid
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700964 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 -0600965 else:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700966 checkExpr.append('skip |= validate_required_pointer("{}", {ppp}"{}"{pps}, {}{}, {});\n'.format(funcPrintName, valuePrintName, prefix, value.name, ptr_required_vuid, **postProcSpec))
Ricardo Garcia98ba9642019-02-19 11:25:18 +0100967 else:
968 # Special case for optional internal allocation function pointers.
969 if (value.type, value.name) == ('PFN_vkInternalAllocationNotification', 'pfnInternalAllocation'):
Ricardo Garciac52489a2019-04-02 18:15:20 +0200970 checkExpr.extend(self.internalAllocationCheck(funcPrintName, prefix, value.name, 'pfnInternalFree', postProcSpec))
971 elif (value.type, value.name) == ('PFN_vkInternalFreeNotification', 'pfnInternalFree'):
972 checkExpr.extend(self.internalAllocationCheck(funcPrintName, prefix, value.name, 'pfnInternalAllocation', postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600973 return checkExpr
Ricardo Garciac52489a2019-04-02 18:15:20 +0200974
975 #
976 # Generate internal allocation function pointer check.
977 def internalAllocationCheck(self, funcPrintName, prefix, name, complementaryName, postProcSpec):
978 checkExpr = []
979 vuid = '"VUID-VkAllocationCallbacks-pfnInternalAllocation-00635"'
980 checkExpr.append('if ({}{} != NULL)'.format(prefix, name))
981 checkExpr.append('{')
982 local_indent = self.incIndent('')
983 # Function pointers need a reinterpret_cast to void*
984 checkExpr.append(local_indent + 'skip |= validate_required_pointer("{}", {ppp}"{}{}"{pps}, reinterpret_cast<const void*>({}{}), {});\n'.format(funcPrintName, prefix, complementaryName, prefix, complementaryName, vuid, **postProcSpec))
985 checkExpr.append('}\n')
986 return checkExpr
987
Mark Lobodzinski85672672016-10-13 08:36:42 -0600988 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -0600989 # Process struct member validation code, performing name substitution if required
Mark Lobodzinski85672672016-10-13 08:36:42 -0600990 def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
991 # Build format specifier list
992 kwargs = {}
993 if '{postProcPrefix}' in line:
994 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
995 if type(memberDisplayNamePrefix) is tuple:
996 kwargs['postProcPrefix'] = 'ParameterName('
997 else:
998 kwargs['postProcPrefix'] = postProcSpec['ppp']
999 if '{postProcSuffix}' in line:
1000 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
1001 if type(memberDisplayNamePrefix) is tuple:
1002 kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
1003 else:
1004 kwargs['postProcSuffix'] = postProcSpec['pps']
1005 if '{postProcInsert}' in line:
1006 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
1007 if type(memberDisplayNamePrefix) is tuple:
1008 kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
1009 else:
1010 kwargs['postProcInsert'] = postProcSpec['ppi']
1011 if '{funcName}' in line:
1012 kwargs['funcName'] = funcName
1013 if '{valuePrefix}' in line:
1014 kwargs['valuePrefix'] = memberNamePrefix
1015 if '{displayNamePrefix}' in line:
1016 # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
1017 if type(memberDisplayNamePrefix) is tuple:
1018 kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
1019 else:
1020 kwargs['displayNamePrefix'] = memberDisplayNamePrefix
1021
1022 if kwargs:
1023 # Need to escape the C++ curly braces
1024 if 'IndexVector' in line:
1025 line = line.replace('IndexVector{ ', 'IndexVector{{ ')
1026 line = line.replace(' }),', ' }}),')
1027 return line.format(**kwargs)
1028 return line
1029 #
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001030 # Process struct member validation code, stripping metadata
1031 def ScrubStructCode(self, code):
1032 scrubbed_lines = ''
1033 for line in code:
1034 if 'validate_struct_pnext' in line:
1035 continue
1036 if 'allowed_structs' in line:
1037 continue
1038 if 'xml-driven validation' in line:
1039 continue
1040 line = line.replace('{postProcPrefix}', '')
1041 line = line.replace('{postProcSuffix}', '')
1042 line = line.replace('{postProcInsert}', '')
1043 line = line.replace('{funcName}', '')
1044 line = line.replace('{valuePrefix}', '')
1045 line = line.replace('{displayNamePrefix}', '')
1046 line = line.replace('{IndexVector}', '')
1047 line = line.replace('local_data->', '')
1048 scrubbed_lines += line
1049 return scrubbed_lines
1050 #
Mark Lobodzinski85672672016-10-13 08:36:42 -06001051 # Process struct validation code for inclusion in function or parent struct validation code
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001052 def expandStructCode(self, item_type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
1053 lines = self.validatedStructs[item_type]
Mark Lobodzinski85672672016-10-13 08:36:42 -06001054 for line in lines:
1055 if output:
1056 output[-1] += '\n'
1057 if type(line) is list:
1058 for sub in line:
1059 output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1060 else:
1061 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1062 return output
1063 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001064 # Process struct pointer/array validation code, performing name substitution if required
Mark Lobodzinski85672672016-10-13 08:36:42 -06001065 def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
1066 expr = []
1067 expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
1068 expr.append('{')
1069 indent = self.incIndent(None)
1070 if lenValue:
1071 # Need to process all elements in the array
1072 indexName = lenValue.name.replace('Count', 'Index')
1073 expr[-1] += '\n'
Mark Young39389872017-01-19 21:10:49 -07001074 if lenValue.ispointer:
1075 # If the length value is a pointer, de-reference it for the count.
1076 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < *{}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
1077 else:
1078 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001079 expr.append(indent + '{')
1080 indent = self.incIndent(indent)
1081 # Prefix for value name to display in error message
Mark Lobodzinski6f82eb52016-12-05 07:38:41 -07001082 if value.ispointer == 2:
1083 memberNamePrefix = '{}{}[{}]->'.format(prefix, value.name, indexName)
1084 memberDisplayNamePrefix = ('{}[%i]->'.format(valueDisplayName), indexName)
1085 else:
1086 memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
1087 memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001088 else:
1089 memberNamePrefix = '{}{}->'.format(prefix, value.name)
1090 memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001091 # Expand the struct validation lines
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001092 expr = self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001093 if lenValue:
1094 # Close if and for scopes
1095 indent = self.decIndent(indent)
1096 expr.append(indent + '}\n')
1097 expr.append('}\n')
1098 return expr
1099 #
1100 # Generate the parameter checking code
1101 def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
1102 lines = [] # Generated lines of code
1103 unused = [] # Unused variable names
1104 for value in values:
1105 usedLines = []
1106 lenParam = None
1107 #
1108 # 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.
1109 postProcSpec = {}
1110 postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
1111 postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
1112 postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
1113 #
1114 # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name
1115 valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
1116 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001117 # Check for NULL pointers, ignore the in-out count parameters that
Mark Lobodzinski85672672016-10-13 08:36:42 -06001118 # will be validated with their associated array
1119 if (value.ispointer or value.isstaticarray) and not value.iscount:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001120 # Parameters for function argument generation
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001121 req = 'true' # Parameter cannot be NULL
Mark Lobodzinski85672672016-10-13 08:36:42 -06001122 cpReq = 'true' # Count pointer cannot be NULL
1123 cvReq = 'true' # Count value cannot be 0
1124 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
Mark Lobodzinski85672672016-10-13 08:36:42 -06001125 # Generate required/optional parameter strings for the pointer and count values
1126 if value.isoptional:
1127 req = 'false'
1128 if value.len:
1129 # The parameter is an array with an explicit count parameter
1130 lenParam = self.getLenParam(values, value.len)
1131 lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
1132 if lenParam.ispointer:
1133 # Count parameters that are pointers are inout
1134 if type(lenParam.isoptional) is list:
1135 if lenParam.isoptional[0]:
1136 cpReq = 'false'
1137 if lenParam.isoptional[1]:
1138 cvReq = 'false'
1139 else:
1140 if lenParam.isoptional:
1141 cpReq = 'false'
1142 else:
1143 if lenParam.isoptional:
1144 cvReq = 'false'
1145 #
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001146 # The parameter will not be processed when tagged as 'noautovalidity'
Mark Lobodzinski85672672016-10-13 08:36:42 -06001147 # For the pointer to struct case, the struct pointer will not be validated, but any
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001148 # members not tagged as 'noautovalidity' will be validated
1149 # We special-case the custom allocator checks, as they are explicit but can be auto-generated.
Ricardo Garcia98ba9642019-02-19 11:25:18 +01001150 AllocatorFunctions = ['PFN_vkAllocationFunction', 'PFN_vkReallocationFunction', 'PFN_vkFreeFunction', 'PFN_vkInternalAllocationNotification', 'PFN_vkInternalFreeNotification']
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001151 if value.noautovalidity and value.type not in AllocatorFunctions:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001152 # 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 Lobodzinski85672672016-10-13 08:36:42 -06001155 if value.type in self.structTypes:
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001156 # If this is a pointer to a struct with an sType field, verify the type
Mark Lobodzinski9cf24dd2017-06-28 14:23:22 -06001157 usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001158 # 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
1159 elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
1160 usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1161 elif value.type in self.flags and value.isconst:
1162 usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1163 elif value.isbool and value.isconst:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001164 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 -06001165 elif value.israngedenum and value.isconst:
Mark Lobodzinskiaff801e2017-07-25 15:29:57 -06001166 enum_value_list = 'All%sEnums' % value.type
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001167 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 -07001168 elif value.name == 'pNext' and value.isconst:
Mark Lobodzinski9ddf9282018-05-31 13:59:59 -06001169 usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001170 else:
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -06001171 usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001172 # 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 -06001173 if value.type in self.validatedStructs:
1174 if value.isconst: # or value.type in self.returnedonly_structs:
1175 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
1176 elif value.type in self.returnedonly_structs:
1177 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001178 # Non-pointer types
1179 else:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001180 # The parameter will not be processes when tagged as 'noautovalidity'
1181 # For the struct case, the struct type will not be validated, but any
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001182 # members not tagged as 'noautovalidity' will be validated
Mark Lobodzinski85672672016-10-13 08:36:42 -06001183 if value.noautovalidity:
1184 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1185 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1186 else:
Mark Lobodzinski024b2822017-06-27 13:22:05 -06001187 vuid_name_tag = structTypeName if structTypeName is not None else funcName
Mark Lobodzinski85672672016-10-13 08:36:42 -06001188 if value.type in self.structTypes:
1189 stype = self.structTypes[value.type]
Dave Houlton413a6782018-05-22 13:01:54 -06001190 vuid = self.GetVuid(value.type, "sType-sType")
Mark Lobodzinskia16ebc72018-06-15 14:47:39 -06001191 undefined_vuid = '"kVUIDUndefined"'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001192 usedLines.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false, kVUIDUndefined, {});\n'.format(
Mike Schuchardt24ac4e72018-08-11 17:37:20 -07001193 funcName, valueDisplayName, valuePrefix, vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001194 elif value.type in self.handleTypes:
1195 if not self.isHandleOptional(value, None):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001196 usedLines.append('skip |= validate_required_handle("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001197 elif value.type in self.flags:
1198 flagBitsName = value.type.replace('Flags', 'FlagBits')
1199 if not flagBitsName in self.flagBits:
Dave Houlton413a6782018-05-22 13:01:54 -06001200 vuid = self.GetVuid(vuid_name_tag, "%s-zerobitmask" % (value.name))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001201 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 -06001202 else:
Mark Lobodzinskie643fcc2017-06-26 16:27:15 -06001203 if value.isoptional:
1204 flagsRequired = 'false'
Dave Houlton413a6782018-05-22 13:01:54 -06001205 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinskie643fcc2017-06-26 16:27:15 -06001206 else:
1207 flagsRequired = 'true'
Dave Houlton413a6782018-05-22 13:01:54 -06001208 vuid = self.GetVuid(vuid_name_tag, "%s-requiredbitmask" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001209 allFlagsName = 'All' + flagBitsName
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001210 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 -06001211 elif value.type in self.flagBits:
1212 flagsRequired = 'false' if value.isoptional else 'true'
1213 allFlagsName = 'All' + value.type
Dave Houlton413a6782018-05-22 13:01:54 -06001214 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001215 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 -06001216 elif value.isbool:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001217 usedLines.append('skip |= validate_bool32("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001218 elif value.israngedenum:
Dave Houlton413a6782018-05-22 13:01:54 -06001219 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinski74cb45f2017-07-25 15:10:29 -06001220 enum_value_list = 'All%sEnums' % value.type
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001221 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 -06001222 # If this is a struct, see if it contains members that need to be checked
1223 if value.type in self.validatedStructs:
1224 memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
1225 memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001226 usedLines.append(self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001227 # Append the parameter check to the function body for the current command
1228 if usedLines:
1229 # Apply special conditional checks
1230 if value.condition:
1231 usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
1232 lines += usedLines
1233 elif not value.iscount:
1234 # If no expression was generated for this value, it is unreferenced by the validation function, unless
1235 # it is an array count, which is indirectly referenced for array valiadation.
1236 unused.append(value.name)
Mark Lobodzinskid4950072017-08-01 13:02:20 -06001237 if not lines:
1238 lines.append('// No xml-driven validation\n')
Mark Lobodzinski85672672016-10-13 08:36:42 -06001239 return lines, unused
1240 #
1241 # Generate the struct member check code from the captured data
1242 def processStructMemberData(self):
1243 indent = self.incIndent(None)
1244 for struct in self.structMembers:
1245 #
1246 # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
1247 lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
1248 if lines:
1249 self.validatedStructs[struct.name] = lines
1250 #
1251 # Generate the command param check code from the captured data
1252 def processCmdData(self):
1253 indent = self.incIndent(None)
1254 for command in self.commands:
1255 # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
1256 startIndex = 0 if command.name == 'vkCreateInstance' else 1
1257 lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
Mark Lobodzinski3f10bfe2017-08-23 15:23:23 -06001258 # Cannot validate extension dependencies for device extension APIs having a physical device as their dispatchable object
Mike Schuchardtafd00482017-08-24 15:15:02 -06001259 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 -06001260 ext_test = ''
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001261 if command.params[0].type in ["VkInstance", "VkPhysicalDevice"] or command.name == 'vkCreateInstance':
1262 ext_table_type = 'instance'
1263 else:
1264 ext_table_type = 'device'
Mike Schuchardtafd00482017-08-24 15:15:02 -06001265 for ext in self.required_extensions[command.name]:
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001266 ext_name_define = ''
1267 ext_enable_name = ''
1268 for extension in self.registry.extensions:
1269 if extension.attrib['name'] == ext:
1270 ext_name_define = extension[0][1].get('name')
1271 ext_enable_name = ext_name_define.lower()
1272 ext_enable_name = re.sub('_extension_name', '', ext_enable_name)
1273 break
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001274 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 -06001275 lines.insert(0, ext_test)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001276 if lines:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001277 func_sig = self.getCmdDef(command) + ' {\n'
1278 func_sig = func_sig.split('VKAPI_CALL vk')[1]
1279 cmdDef = 'bool StatelessValidation::PreCallValidate' + func_sig
Mark Lobodzinskid4950072017-08-01 13:02:20 -06001280 cmdDef += '%sbool skip = false;\n' % indent
Mark Lobodzinski70d416b2019-07-08 15:59:43 -06001281 for line in lines:
1282 if type(line) is list:
1283 for sub in line:
1284 cmdDef += indent + sub
1285 else:
1286 cmdDef += indent + line
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001287 # Insert call to custom-written function if present
1288 if command.name in self.functions_with_manual_checks:
1289 # Generate parameter list for manual fcn and down-chain calls
1290 params_text = ''
1291 for param in command.params:
1292 params_text += '%s, ' % param.name
1293 params_text = params_text[:-2] + ');\n'
1294 cmdDef += ' skip |= manual_PreCallValidate'+ command.name[2:] + '(' + params_text
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001295 cmdDef += '%sreturn skip;\n' % indent
Mark Lobodzinski85672672016-10-13 08:36:42 -06001296 cmdDef += '}\n'
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001297 self.validation.append(cmdDef)