blob: 74a911a152fbe3e14bd16192a1ebc0fd104ddb74 [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',
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700181 ]
182
Mark Lobodzinski85672672016-10-13 08:36:42 -0600183 # Commands to ignore
184 self.blacklist = [
185 'vkGetInstanceProcAddr',
186 'vkGetDeviceProcAddr',
Mark Young6ba8abe2017-11-09 10:37:04 -0700187 'vkEnumerateInstanceVersion',
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600188 'vkEnumerateInstanceLayerProperties',
189 'vkEnumerateInstanceExtensionProperties',
190 'vkEnumerateDeviceLayerProperties',
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600191 'vkEnumerateDeviceExtensionProperties',
Mike Schuchardt21638df2019-03-16 10:52:02 -0700192 'vkGetDeviceGroupSurfacePresentModes2EXT'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600193 ]
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700194
Dustin Gravesce68f082017-03-30 15:42:16 -0600195 # Structure fields to ignore
196 self.structMemberBlacklist = { 'VkWriteDescriptorSet' : ['dstSet'] }
Mark Lobodzinski85672672016-10-13 08:36:42 -0600197 # Validation conditions for some special case struct members that are conditionally validated
198 self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } }
199 # Header version
200 self.headerVersion = None
201 # Internal state - accumulators for different inner block text
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600202 self.validation = [] # Text comprising the main per-api parameter validation routines
Mark Lobodzinski85672672016-10-13 08:36:42 -0600203 self.stypes = [] # Values from the VkStructureType enumeration
204 self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType
205 self.handleTypes = set() # Set of handle type names
206 self.commands = [] # List of CommandData records for all Vulkan commands
207 self.structMembers = [] # List of StructMemberData records for all Vulkan structs
208 self.validatedStructs = dict() # Map of structs type names to generated validation code for that struct type
209 self.enumRanges = dict() # Map of enum name to BEGIN/END range values
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600210 self.enumValueLists = '' # String containing enumerated type map definitions
Mark Lobodzinski85672672016-10-13 08:36:42 -0600211 self.flags = set() # Map of flags typenames
212 self.flagBits = dict() # Map of flag bits typename to list of values
Chris Forbes78ea32d2016-11-28 11:14:17 +1300213 self.newFlags = set() # Map of flags typenames /defined in the current feature/
Mike Schuchardtafd00482017-08-24 15:15:02 -0600214 self.required_extensions = dict() # Dictionary of required extensions for each item in the current extension
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600215 self.extension_type = '' # Type of active feature (extension), device or instance
216 self.extension_names = dict() # Dictionary of extension names to extension name defines
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600217 self.structextends_list = [] # List of extensions which extend another struct
218 self.struct_feature_protect = dict() # Dictionary of structnames and FeatureExtraProtect strings
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600219 self.valid_vuids = set() # Set of all valid VUIDs
Dave Houlton413a6782018-05-22 13:01:54 -0600220 self.vuid_dict = dict() # VUID dictionary (from JSON)
221 self.alias_dict = dict() # Dict of cmd|struct aliases
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700222 self.header_file = False # Header file generation flag
223 self.source_file = False # Source file generation flag
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600224 self.returnedonly_structs = []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600225 # Named tuples to store struct and command data
Mark Lobodzinski85672672016-10-13 08:36:42 -0600226 self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum',
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600227 'isconst', 'isoptional', 'iscount', 'noautovalidity',
228 'len', 'extstructs', 'condition', 'cdecl'])
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600229 self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl', 'extension_type', 'result'])
Mark Lobodzinski85672672016-10-13 08:36:42 -0600230 self.StructMemberData = namedtuple('StructMemberData', ['name', 'members'])
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600231
Mark Lobodzinski85672672016-10-13 08:36:42 -0600232 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600233 # Generate Copyright comment block for file
234 def GenerateCopyright(self):
235 copyright = '/* *** THIS FILE IS GENERATED - DO NOT EDIT! ***\n'
236 copyright += ' * See parameter_validation_generator.py for modifications\n'
237 copyright += ' *\n'
Dave Houlton413a6782018-05-22 13:01:54 -0600238 copyright += ' * Copyright (c) 2015-2018 The Khronos Group Inc.\n'
239 copyright += ' * Copyright (c) 2015-2018 LunarG, Inc.\n'
240 copyright += ' * Copyright (C) 2015-2018 Google Inc.\n'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600241 copyright += ' *\n'
242 copyright += ' * Licensed under the Apache License, Version 2.0 (the "License");\n'
243 copyright += ' * you may not use this file except in compliance with the License.\n'
244 copyright += ' * Copyright (c) 2015-2017 Valve Corporation\n'
245 copyright += ' * You may obtain a copy of the License at\n'
246 copyright += ' *\n'
247 copyright += ' * http://www.apache.org/licenses/LICENSE-2.0\n'
248 copyright += ' *\n'
249 copyright += ' * Unless required by applicable law or agreed to in writing, software\n'
250 copyright += ' * distributed under the License is distributed on an "AS IS" BASIS,\n'
251 copyright += ' * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n'
252 copyright += ' * See the License for the specific language governing permissions and\n'
253 copyright += ' * limitations under the License.\n'
254 copyright += ' *\n'
255 copyright += ' * Author: Mark Lobodzinski <mark@LunarG.com>\n'
Dave Houlton413a6782018-05-22 13:01:54 -0600256 copyright += ' * Author: Dave Houlton <daveh@LunarG.com>\n'
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600257 copyright += ' */\n\n'
258 return copyright
259 #
260 # Increases the global indent variable
Mark Lobodzinski85672672016-10-13 08:36:42 -0600261 def incIndent(self, indent):
262 inc = ' ' * self.INDENT_SPACES
263 if indent:
264 return indent + inc
265 return inc
266 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600267 # Decreases the global indent variable
Mark Lobodzinski85672672016-10-13 08:36:42 -0600268 def decIndent(self, indent):
269 if indent and (len(indent) > self.INDENT_SPACES):
270 return indent[:-self.INDENT_SPACES]
271 return ''
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600272 #
Dave Houlton413a6782018-05-22 13:01:54 -0600273 # Walk the JSON-derived dict and find all "vuid" key values
274 def ExtractVUIDs(self, d):
275 if hasattr(d, 'items'):
276 for k, v in d.items():
277 if k == "vuid":
278 yield v
279 elif isinstance(v, dict):
280 for s in self.ExtractVUIDs(v):
281 yield s
282 elif isinstance (v, list):
283 for l in v:
284 for s in self.ExtractVUIDs(l):
285 yield s
Mark Lobodzinski85672672016-10-13 08:36:42 -0600286 #
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600287 # Called at file creation time
Mark Lobodzinski85672672016-10-13 08:36:42 -0600288 def beginFile(self, genOpts):
289 OutputGenerator.beginFile(self, genOpts)
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700290 self.header_file = (genOpts.filename == 'parameter_validation.h')
291 self.source_file = (genOpts.filename == 'parameter_validation.cpp')
292
293 if not self.header_file and not self.source_file:
294 print("Error: Output Filenames have changed, update generator source.\n")
295 sys.exit(1)
296
297 if self.source_file or self.header_file:
298 # Output Copyright text
299 s = self.GenerateCopyright()
300 write(s, file=self.outFile)
301
302 if self.header_file:
303 return
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -0600304
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700305 # Build map of structure type names to VkStructureType enum values
306 # Find all types of category "struct"
307 for struct in self.registry.tree.iterfind('types/type[@category="struct"]'):
308 # Check if struct has member named "sType" of type "VkStructureType" which has values defined
309 stype = struct.find('member[name="sType"][type="VkStructureType"][@values]')
Shahbaz Youssefi23aee922019-01-11 14:04:49 -0500310 if stype is not None:
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700311 # Store VkStructureType value for this type
312 self.structTypes[struct.get('name')] = stype.get('values')
313
Mark Lobodzinski27a9e7c2018-05-31 16:01:57 -0600314 self.valid_usage_path = genOpts.valid_usage_path
315 vu_json_filename = os.path.join(self.valid_usage_path + os.sep, 'validusage.json')
316 if os.path.isfile(vu_json_filename):
317 json_file = open(vu_json_filename, 'r')
318 self.vuid_dict = json.load(json_file)
319 json_file.close()
320 if len(self.vuid_dict) == 0:
321 print("Error: Could not find, or error loading %s/validusage.json\n", vu_json_filename)
322 sys.exit(1)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600323 #
Dave Houlton413a6782018-05-22 13:01:54 -0600324 # Build a set of all vuid text strings found in validusage.json
325 for json_vuid_string in self.ExtractVUIDs(self.vuid_dict):
326 self.valid_vuids.add(json_vuid_string)
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600327 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600328 # Headers
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700329 write('#include "chassis.h"', file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600330 self.newline()
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700331 write('#include "stateless_validation.h"', file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600332 self.newline()
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600333 #
334 # Called at end-time for final content output
Mark Lobodzinski85672672016-10-13 08:36:42 -0600335 def endFile(self):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700336 if self.source_file:
337 # C-specific
338 self.newline()
339 write(self.enumValueLists, file=self.outFile)
340 self.newline()
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600341
Locke6b6b7382019-04-16 15:08:49 -0600342 pnext_handler = 'bool StatelessValidation::ValidatePnextStructContents(const char *api_name, const ParameterName &parameter_name, const VkBaseOutStructure* header) {\n'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700343 pnext_handler += ' bool skip = false;\n'
344 pnext_handler += ' switch(header->sType) {\n'
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600345
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700346 # Do some processing here to extract data from validatedstructs...
347 for item in self.structextends_list:
348 postProcSpec = {}
349 postProcSpec['ppp'] = '' if not item else '{postProcPrefix}'
350 postProcSpec['pps'] = '' if not item else '{postProcSuffix}'
351 postProcSpec['ppi'] = '' if not item else '{postProcInsert}'
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600352
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700353 pnext_case = '\n'
354 protect = ''
355 # Guard struct cases with feature ifdefs, if necessary
356 if item in self.struct_feature_protect.keys():
357 protect = self.struct_feature_protect[item]
358 pnext_case += '#ifdef %s\n' % protect
359 pnext_case += ' // Validation code for %s structure members\n' % item
360 pnext_case += ' case %s: {\n' % self.structTypes[item]
361 pnext_case += ' %s *structure = (%s *) header;\n' % (item, item)
362 expr = self.expandStructCode(item, item, 'structure->', '', ' ', [], postProcSpec)
363 struct_validation_source = self.ScrubStructCode(expr)
364 pnext_case += '%s' % struct_validation_source
365 pnext_case += ' } break;\n'
Raul Tambre7b300182019-05-04 11:25:14 +0300366 if protect:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700367 pnext_case += '#endif // %s\n' % protect
368 # Skip functions containing no validation
Raul Tambre7b300182019-05-04 11:25:14 +0300369 if struct_validation_source:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700370 pnext_handler += pnext_case;
371 pnext_handler += ' default:\n'
372 pnext_handler += ' skip = false;\n'
373 pnext_handler += ' }\n'
374 pnext_handler += ' return skip;\n'
375 pnext_handler += '}\n'
376 write(pnext_handler, file=self.outFile)
377 self.newline()
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600378
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700379 ext_template = 'bool StatelessValidation::OutputExtensionError(const std::string &api_name, const std::string &extension_name) {\n'
380 ext_template += ' return log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,\n'
381 ext_template += ' kVUID_PVError_ExtensionNotEnabled, "Attemped to call %s() but its required extension %s has not been enabled\\n",\n'
382 ext_template += ' api_name.c_str(), extension_name.c_str());\n'
383 ext_template += '}\n'
384 write(ext_template, file=self.outFile)
385 self.newline()
386 commands_text = '\n'.join(self.validation)
387 write(commands_text, file=self.outFile)
388 self.newline()
389 if self.header_file:
390 # Output declarations and record intercepted procedures
391 write('\n'.join(self.declarations), file=self.outFile)
392 # Finish processing in superclass
393 OutputGenerator.endFile(self)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600394 #
395 # Processing at beginning of each feature or extension
Mark Lobodzinski85672672016-10-13 08:36:42 -0600396 def beginFeature(self, interface, emit):
397 # Start processing in superclass
398 OutputGenerator.beginFeature(self, interface, emit)
399 # C-specific
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600400 # Accumulate includes, defines, types, enums, function pointer typedefs, end function prototypes separately for this
401 # feature. They're only printed in endFeature().
Mark Lobodzinski85672672016-10-13 08:36:42 -0600402 self.headerVersion = None
Mark Lobodzinski85672672016-10-13 08:36:42 -0600403 self.stypes = []
Mark Lobodzinski85672672016-10-13 08:36:42 -0600404 self.commands = []
405 self.structMembers = []
Chris Forbes78ea32d2016-11-28 11:14:17 +1300406 self.newFlags = set()
Mark Lobodzinski62f71562017-10-24 13:41:18 -0600407 self.featureExtraProtect = GetFeatureProtect(interface)
Mike Schuchardtafd00482017-08-24 15:15:02 -0600408 # Get base list of extension dependencies for all items in this extension
409 base_required_extensions = []
Mark Lobodzinski31964ca2017-09-18 14:15:09 -0600410 if "VK_VERSION_1" not in self.featureName:
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600411 # Save Name Define to get correct enable name later
412 nameElem = interface[0][1]
413 name = nameElem.get('name')
414 self.extension_names[self.featureName] = name
415 # This extension is the first dependency for this command
Mike Schuchardtafd00482017-08-24 15:15:02 -0600416 base_required_extensions.append(self.featureName)
417 # Add any defined extension dependencies to the base dependency list for this extension
418 requires = interface.get('requires')
419 if requires is not None:
420 base_required_extensions.extend(requires.split(','))
Mike Schuchardtafd00482017-08-24 15:15:02 -0600421 # Build dictionary of extension dependencies for each item in this extension
422 self.required_extensions = dict()
423 for require_element in interface.findall('require'):
424 # Copy base extension dependency list
425 required_extensions = list(base_required_extensions)
426 # Add any additional extension dependencies specified in this require block
427 additional_extensions = require_element.get('extension')
428 if additional_extensions:
429 required_extensions.extend(additional_extensions.split(','))
430 # Save full extension list for all named items
431 for element in require_element.findall('*[@name]'):
432 self.required_extensions[element.get('name')] = required_extensions
433
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600434 # And note if this is an Instance or Device extension
435 self.extension_type = interface.get('type')
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600436 #
437 # Called at the end of each extension (feature)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600438 def endFeature(self):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700439 if self.header_file:
440 return
Mark Lobodzinski85672672016-10-13 08:36:42 -0600441 # C-specific
442 # Actually write the interface to the output file.
443 if (self.emit):
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600444 # 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 -0600445 # or move it below the 'for section...' loop.
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600446 ifdef = ''
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100447 if (self.featureExtraProtect is not None):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600448 ifdef = '#ifdef %s\n' % self.featureExtraProtect
449 self.validation.append(ifdef)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600450 # Generate the struct member checking code from the captured data
451 self.processStructMemberData()
452 # Generate the command parameter checking code from the captured data
453 self.processCmdData()
454 # Write the declaration for the HeaderVersion
455 if self.headerVersion:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700456 write('const uint32_t GeneratedVulkanHeaderVersion = {};'.format(self.headerVersion), file=self.outFile)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600457 self.newline()
458 # Write the declarations for the VkFlags values combining all flag bits
Chris Forbes78ea32d2016-11-28 11:14:17 +1300459 for flag in sorted(self.newFlags):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600460 flagBits = flag.replace('Flags', 'FlagBits')
461 if flagBits in self.flagBits:
462 bits = self.flagBits[flagBits]
463 decl = 'const {} All{} = {}'.format(flag, flagBits, bits[0])
464 for bit in bits[1:]:
465 decl += '|' + bit
466 decl += ';'
467 write(decl, file=self.outFile)
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600468 endif = '\n'
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100469 if (self.featureExtraProtect is not None):
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -0600470 endif = '#endif // %s\n' % self.featureExtraProtect
471 self.validation.append(endif)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600472 # Finish processing in superclass
473 OutputGenerator.endFeature(self)
474 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600475 # Type generation
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700476 def genType(self, typeinfo, name, alias):
Dave Houlton413a6782018-05-22 13:01:54 -0600477 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100478 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600479 self.alias_dict[name]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700480 OutputGenerator.genType(self, typeinfo, name, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600481 typeElem = typeinfo.elem
Mark Lobodzinski87017df2018-05-30 11:29:24 -0600482 # 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 -0600483 category = typeElem.get('category')
484 if (category == 'struct' or category == 'union'):
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700485 self.genStruct(typeinfo, name, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600486 elif (category == 'handle'):
487 self.handleTypes.add(name)
488 elif (category == 'bitmask'):
489 self.flags.add(name)
Chris Forbes78ea32d2016-11-28 11:14:17 +1300490 self.newFlags.add(name)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600491 elif (category == 'define'):
492 if name == 'VK_HEADER_VERSION':
493 nameElem = typeElem.find('name')
494 self.headerVersion = noneStr(nameElem.tail).strip()
495 #
496 # Struct parameter check generation.
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600497 # This is a special case of the <type> tag where the contents are interpreted as a set of <member> tags instead of freeform C
498 # type declarations. The <member> tags are just like <param> tags - they are a declaration of a struct or union member.
499 # Only simple member declarations are supported (no nested structs etc.)
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700500 def genStruct(self, typeinfo, typeName, alias):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700501 if not self.source_file:
502 return
Dave Houlton413a6782018-05-22 13:01:54 -0600503 # alias has already been recorded in genType, above
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700504 OutputGenerator.genStruct(self, typeinfo, typeName, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600505 conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None
506 members = typeinfo.elem.findall('.//member')
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600507 if self.featureExtraProtect is not None:
508 self.struct_feature_protect[typeName] = self.featureExtraProtect
Mark Lobodzinski85672672016-10-13 08:36:42 -0600509 #
510 # Iterate over members once to get length parameters for arrays
511 lens = set()
512 for member in members:
513 len = self.getLen(member)
514 if len:
515 lens.add(len)
516 #
517 # Generate member info
518 membersInfo = []
519 for member in members:
520 # Get the member's type and name
521 info = self.getTypeNameTuple(member)
522 type = info[0]
523 name = info[1]
524 stypeValue = ''
525 cdecl = self.makeCParamDecl(member, 0)
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700526
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600527 # Store pointer/array/string info -- Check for parameter name in lens set
Mark Lobodzinski85672672016-10-13 08:36:42 -0600528 iscount = False
529 if name in lens:
530 iscount = True
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600531 # The pNext members are not tagged as optional, but are treated as optional for parameter NULL checks. Static array
532 # members are also treated as optional to skip NULL pointer validation, as they won't be NULL.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600533 isstaticarray = self.paramIsStaticArray(member)
534 isoptional = False
535 if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray):
536 isoptional = True
Dustin Gravesce68f082017-03-30 15:42:16 -0600537 # Determine if value should be ignored by code generation.
538 noautovalidity = False
539 if (member.attrib.get('noautovalidity') is not None) or ((typeName in self.structMemberBlacklist) and (name in self.structMemberBlacklist[typeName])):
540 noautovalidity = True
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600541 structextends = False
Mark Lobodzinski85672672016-10-13 08:36:42 -0600542 membersInfo.append(self.CommandParam(type=type, name=name,
543 ispointer=self.paramIsPointer(member),
544 isstaticarray=isstaticarray,
545 isbool=True if type == 'VkBool32' else False,
546 israngedenum=True if type in self.enumRanges else False,
547 isconst=True if 'const' in cdecl else False,
548 isoptional=isoptional,
549 iscount=iscount,
Dustin Gravesce68f082017-03-30 15:42:16 -0600550 noautovalidity=noautovalidity,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600551 len=self.getLen(member),
Mike Schuchardta40d0b02017-07-23 12:47:47 -0600552 extstructs=self.registry.validextensionstructs[typeName] if name == 'pNext' else None,
Mark Lobodzinski85672672016-10-13 08:36:42 -0600553 condition=conditions[name] if conditions and name in conditions else None,
554 cdecl=cdecl))
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600555 # If this struct extends another, keep its name in list for further processing
556 if typeinfo.elem.attrib.get('structextends') is not None:
557 self.structextends_list.append(typeName)
558 # Returnedonly structs should have most of their members ignored -- on entry, we only care about validating the sType and
559 # pNext members. Everything else will be overwritten by the callee.
560 if typeinfo.elem.attrib.get('returnedonly') is not None:
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600561 self.returnedonly_structs.append(typeName)
562 membersInfo = [m for m in membersInfo if m.name in ('sType', 'pNext')]
Mark Lobodzinski85672672016-10-13 08:36:42 -0600563 self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo))
564 #
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600565 # Capture group (e.g. C "enum" type) info to be used for param check code generation.
Mark Lobodzinski85672672016-10-13 08:36:42 -0600566 # These are concatenated together with other types.
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700567 def genGroup(self, groupinfo, groupName, alias):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700568 if not self.source_file:
569 return
Dave Houlton413a6782018-05-22 13:01:54 -0600570 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100571 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600572 self.alias_dict[groupName]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700573 OutputGenerator.genGroup(self, groupinfo, groupName, alias)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600574 groupElem = groupinfo.elem
Mark Lobodzinski85672672016-10-13 08:36:42 -0600575 # Store the sType values
576 if groupName == 'VkStructureType':
577 for elem in groupElem.findall('enum'):
578 self.stypes.append(elem.get('name'))
579 elif 'FlagBits' in groupName:
580 bits = []
581 for elem in groupElem.findall('enum'):
Shannon McPherson533a66c2018-08-21 12:09:25 -0600582 if elem.get('supported') != 'disabled':
583 bits.append(elem.get('name'))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600584 if bits:
585 self.flagBits[groupName] = bits
586 else:
587 # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check)
588 expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper()
589 expandPrefix = expandName
590 expandSuffix = ''
591 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
592 if expandSuffixMatch:
593 expandSuffix = '_' + expandSuffixMatch.group()
594 # Strip off the suffix from the prefix
595 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
596 isEnum = ('FLAG_BITS' not in expandPrefix)
597 if isEnum:
598 self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix)
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600599 # Create definition for a list containing valid enum values for this enumerated type
Mike Schuchardt21638df2019-03-16 10:52:02 -0700600 if self.featureExtraProtect is not None:
601 enum_entry = '\n#ifdef %s\n' % self.featureExtraProtect
602 else:
603 enum_entry = ''
604 enum_entry += 'const std::vector<%s> All%sEnums = {' % (groupName, groupName)
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600605 for enum in groupElem:
606 name = enum.get('name')
Mark Lobodzinski117d88f2017-07-27 12:09:08 -0600607 if name is not None and enum.get('supported') != 'disabled':
608 enum_entry += '%s, ' % name
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600609 enum_entry += '};\n'
Mike Schuchardt21638df2019-03-16 10:52:02 -0700610 if self.featureExtraProtect is not None:
611 enum_entry += '#endif // %s' % self.featureExtraProtect
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600612 self.enumValueLists += enum_entry
Mark Lobodzinski85672672016-10-13 08:36:42 -0600613 #
Mark Lobodzinskif31e0422017-07-25 14:29:42 -0600614 # Capture command parameter info to be used for param check code generation.
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700615 def genCmd(self, cmdinfo, name, alias):
Dave Houlton413a6782018-05-22 13:01:54 -0600616 # record the name/alias pair
Michał Janiszewski3c3ce9e2018-10-30 23:25:21 +0100617 if alias is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600618 self.alias_dict[name]=alias
Mike Schuchardtf375c7c2017-12-28 11:23:48 -0700619 OutputGenerator.genCmd(self, cmdinfo, name, alias)
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600620 decls = self.makeCDecls(cmdinfo.elem)
621 typedef = decls[1]
622 typedef = typedef.split(')',1)[1]
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700623 if self.header_file:
624 if name not in self.blacklist:
625 if (self.featureExtraProtect is not None):
626 self.declarations += [ '#ifdef %s' % self.featureExtraProtect ]
627 # Strip off 'vk' from API name
628 self.declarations += [ '%s%s' % ('bool PreCallValidate', decls[0].split("VKAPI_CALL vk")[1])]
629 if (self.featureExtraProtect is not None):
630 self.declarations += [ '#endif' ]
631 if self.source_file:
632 if name not in self.blacklist:
633 params = cmdinfo.elem.findall('param')
634 # Get list of array lengths
635 lens = set()
636 for param in params:
637 len = self.getLen(param)
638 if len:
639 lens.add(len)
640 # Get param info
641 paramsInfo = []
642 for param in params:
643 paramInfo = self.getTypeNameTuple(param)
644 cdecl = self.makeCParamDecl(param, 0)
645 # Check for parameter name in lens set
646 iscount = False
647 if paramInfo[1] in lens:
648 iscount = True
649 paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1],
650 ispointer=self.paramIsPointer(param),
651 isstaticarray=self.paramIsStaticArray(param),
652 isbool=True if paramInfo[0] == 'VkBool32' else False,
653 israngedenum=True if paramInfo[0] in self.enumRanges else False,
654 isconst=True if 'const' in cdecl else False,
655 isoptional=self.paramIsOptional(param),
656 iscount=iscount,
657 noautovalidity=True if param.attrib.get('noautovalidity') is not None else False,
658 len=self.getLen(param),
659 extstructs=None,
660 condition=None,
661 cdecl=cdecl))
662 # Save return value information, if any
663 result_type = ''
664 resultinfo = cmdinfo.elem.find('proto/type')
665 if (resultinfo is not None and resultinfo.text != 'void'):
666 result_type = resultinfo.text
667 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 -0600668 #
669 # Check if the parameter passed in is a pointer
670 def paramIsPointer(self, param):
671 ispointer = 0
672 paramtype = param.find('type')
673 if (paramtype.tail is not None) and ('*' in paramtype.tail):
674 ispointer = paramtype.tail.count('*')
675 elif paramtype.text[:4] == 'PFN_':
676 # Treat function pointer typedefs as a pointer to a single value
677 ispointer = 1
678 return ispointer
679 #
680 # Check if the parameter passed in is a static array
681 def paramIsStaticArray(self, param):
682 isstaticarray = 0
683 paramname = param.find('name')
684 if (paramname.tail is not None) and ('[' in paramname.tail):
685 isstaticarray = paramname.tail.count('[')
686 return isstaticarray
687 #
688 # Check if the parameter passed in is optional
689 # Returns a list of Boolean values for comma separated len attributes (len='false,true')
690 def paramIsOptional(self, param):
691 # See if the handle is optional
692 isoptional = False
693 # Simple, if it's optional, return true
694 optString = param.attrib.get('optional')
695 if optString:
696 if optString == 'true':
697 isoptional = True
698 elif ',' in optString:
699 opts = []
700 for opt in optString.split(','):
701 val = opt.strip()
702 if val == 'true':
703 opts.append(True)
704 elif val == 'false':
705 opts.append(False)
706 else:
707 print('Unrecognized len attribute value',val)
708 isoptional = opts
709 return isoptional
710 #
711 # Check if the handle passed in is optional
712 # Uses the same logic as ValidityOutputGenerator.isHandleOptional
713 def isHandleOptional(self, param, lenParam):
714 # Simple, if it's optional, return true
715 if param.isoptional:
716 return True
717 # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes.
718 if param.noautovalidity:
719 return True
720 # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional
721 if lenParam and lenParam.isoptional:
722 return True
723 return False
724 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600725 # Retrieve the value of the len tag
726 def getLen(self, param):
727 result = None
728 len = param.attrib.get('len')
729 if len and len != 'null-terminated':
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600730 # For string arrays, 'len' can look like 'count,null-terminated', indicating that we have a null terminated array of
731 # 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 -0600732 if 'null-terminated' in len:
733 result = len.split(',')[0]
734 else:
735 result = len
736 result = str(result).replace('::', '->')
737 return result
738 #
739 # Retrieve the type and name for a parameter
740 def getTypeNameTuple(self, param):
741 type = ''
742 name = ''
743 for elem in param:
744 if elem.tag == 'type':
745 type = noneStr(elem.text)
746 elif elem.tag == 'name':
747 name = noneStr(elem.text)
748 return (type, name)
749 #
750 # Find a named parameter in a parameter list
751 def getParamByName(self, params, name):
752 for param in params:
753 if param.name == name:
754 return param
755 return None
756 #
757 # Extract length values from latexmath. Currently an inflexible solution that looks for specific
758 # patterns that are found in vk.xml. Will need to be updated when new patterns are introduced.
759 def parseLateXMath(self, source):
760 name = 'ERROR'
761 decoratedName = 'ERROR'
762 if 'mathit' in source:
Mark Lobodzinski36c33862017-02-13 10:15:53 -0700763 # Matches expressions similar to 'latexmath:[\lceil{\mathit{rasterizationSamples} \over 32}\rceil]'
764 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 -0600765 if not match or match.group(1) != match.group(4):
766 raise 'Unrecognized latexmath expression'
767 name = match.group(2)
768 decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3))
769 else:
Mark Lobodzinski36c33862017-02-13 10:15:53 -0700770 # Matches expressions similar to 'latexmath : [dataSize \over 4]'
Shannon McPhersonbd68df02018-10-29 15:04:41 -0600771 match = re.match(r'latexmath\s*\:\s*\[\s*(\\textrm\{)?(\w+)\}?\s*\\over\s*(\d+)\s*\]', source)
772 name = match.group(2)
773 decoratedName = '{}/{}'.format(*match.group(2, 3))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600774 return name, decoratedName
775 #
776 # Get the length paramater record for the specified parameter name
777 def getLenParam(self, params, name):
778 lenParam = None
779 if name:
780 if '->' in name:
781 # The count is obtained by dereferencing a member of a struct parameter
782 lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False,
Mark Lobodzinskia1368552018-05-04 16:03:28 -0600783 isstaticarray=None, isoptional=False, type=None, noautovalidity=False,
784 len=None, extstructs=None, condition=None, cdecl=None)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600785 elif 'latexmath' in name:
786 lenName, decoratedName = self.parseLateXMath(name)
787 lenParam = self.getParamByName(params, lenName)
Mark Lobodzinski85672672016-10-13 08:36:42 -0600788 else:
789 lenParam = self.getParamByName(params, name)
790 return lenParam
791 #
792 # Convert a vulkan.h command declaration into a parameter_validation.h definition
793 def getCmdDef(self, cmd):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600794 # Strip the trailing ';' and split into individual lines
795 lines = cmd.cdecl[:-1].split('\n')
Mark Lobodzinskid4950072017-08-01 13:02:20 -0600796 cmd_hdr = '\n'.join(lines)
797 return cmd_hdr
Mark Lobodzinski85672672016-10-13 08:36:42 -0600798 #
799 # Generate the code to check for a NULL dereference before calling the
800 # validation function
801 def genCheckedLengthCall(self, name, exprs):
802 count = name.count('->')
803 if count:
804 checkedExpr = []
805 localIndent = ''
806 elements = name.split('->')
807 # Open the if expression blocks
808 for i in range(0, count):
809 checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1])))
810 localIndent = self.incIndent(localIndent)
811 # Add the validation expression
812 for expr in exprs:
813 checkedExpr.append(localIndent + expr)
814 # Close the if blocks
815 for i in range(0, count):
816 localIndent = self.decIndent(localIndent)
817 checkedExpr.append(localIndent + '}\n')
818 return [checkedExpr]
819 # No if statements were required
820 return exprs
821 #
822 # Generate code to check for a specific condition before executing validation code
823 def genConditionalCall(self, prefix, condition, exprs):
824 checkedExpr = []
825 localIndent = ''
826 formattedCondition = condition.format(prefix)
827 checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition))
828 checkedExpr.append(localIndent + '{\n')
829 localIndent = self.incIndent(localIndent)
830 for expr in exprs:
831 checkedExpr.append(localIndent + expr)
832 localIndent = self.decIndent(localIndent)
833 checkedExpr.append(localIndent + '}\n')
834 return [checkedExpr]
835 #
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600836 # Get VUID identifier from implicit VUID tag
Dave Houlton413a6782018-05-22 13:01:54 -0600837 def GetVuid(self, name, suffix):
838 vuid_string = 'VUID-%s-%s' % (name, suffix)
839 vuid = "kVUIDUndefined"
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600840 if '->' in vuid_string:
Dave Houlton413a6782018-05-22 13:01:54 -0600841 return vuid
842 if vuid_string in self.valid_vuids:
843 vuid = "\"%s\"" % vuid_string
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600844 else:
Dave Houlton413a6782018-05-22 13:01:54 -0600845 if name in self.alias_dict:
846 alias_string = 'VUID-%s-%s' % (self.alias_dict[name], suffix)
847 if alias_string in self.valid_vuids:
Shannon McPherson23d97212019-02-18 13:39:42 -0700848 vuid = "\"%s\"" % alias_string
Mark Lobodzinski06954ea2017-06-21 12:21:45 -0600849 return vuid
850 #
Mark Lobodzinski85672672016-10-13 08:36:42 -0600851 # Generate the sType check string
Mark Lobodzinski9cf24dd2017-06-28 14:23:22 -0600852 def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600853 checkExpr = []
854 stype = self.structTypes[value.type]
Mark Lobodzinski59603552018-05-29 16:14:59 -0600855 vuid_name = struct_type_name if struct_type_name is not None else funcPrintName
856 stype_vuid = self.GetVuid(value.type, "sType-sType")
857 param_vuid = self.GetVuid(vuid_name, "%s-parameter" % value.name)
858
Mark Lobodzinski85672672016-10-13 08:36:42 -0600859 if lenValue:
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800860 count_required_vuid = self.GetVuid(vuid_name, "%s-arraylength" % lenValue.name)
861
Mark Lobodzinski85672672016-10-13 08:36:42 -0600862 # This is an array with a pointer to a count value
863 if lenValue.ispointer:
864 # 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 -0800865 checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {}, {});\n'.format(
866 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 -0600867 # This is an array with an integer count value
868 else:
Jasper St. Pierre6c98f8c2019-01-22 15:18:03 -0800869 checkExpr.append('skip |= validate_struct_type_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {}, {}, {});\n'.format(
870 funcPrintName, lenValueRequired, valueRequired, stype_vuid, param_vuid, count_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype, pf=prefix, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600871 # This is an individual struct
872 else:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700873 checkExpr.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", {}{vn}, {sv}, {}, {}, {});\n'.format(
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700874 funcPrintName, valuePrintName, prefix, valueRequired, param_vuid, stype_vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600875 return checkExpr
876 #
877 # Generate the handle check string
878 def makeHandleCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
879 checkExpr = []
880 if lenValue:
881 if lenValue.ispointer:
882 # This is assumed to be an output array with a pointer to a count value
883 raise('Unsupported parameter validation case: Output handle array elements are not NULL checked')
884 else:
885 # This is an array with an integer count value
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700886 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 -0600887 funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
888 else:
889 # This is assumed to be an output handle pointer
890 raise('Unsupported parameter validation case: Output handles are not NULL checked')
891 return checkExpr
892 #
893 # Generate check string for an array of VkFlags values
894 def makeFlagsArrayCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec):
895 checkExpr = []
896 flagBitsName = value.type.replace('Flags', 'FlagBits')
897 if not flagBitsName in self.flagBits:
898 raise('Unsupported parameter validation case: array of reserved VkFlags')
899 else:
900 allFlags = 'All' + flagBitsName
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700901 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 -0600902 return checkExpr
903 #
904 # Generate pNext check string
Mark Lobodzinski3c828522017-06-26 13:05:57 -0600905 def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600906 checkExpr = []
907 # Generate an array of acceptable VkStructureType values for pNext
908 extStructCount = 0
909 extStructVar = 'NULL'
910 extStructNames = 'NULL'
Dave Houlton413a6782018-05-22 13:01:54 -0600911 vuid = self.GetVuid(struct_type_name, "pNext-pNext")
Mark Lobodzinski85672672016-10-13 08:36:42 -0600912 if value.extstructs:
Mike Schuchardtc73d07e2017-07-12 10:10:01 -0600913 extStructVar = 'allowed_structs_{}'.format(struct_type_name)
914 extStructCount = 'ARRAY_SIZE({})'.format(extStructVar)
Mike Schuchardta40d0b02017-07-23 12:47:47 -0600915 extStructNames = '"' + ', '.join(value.extstructs) + '"'
Mike Schuchardt24ac4e72018-08-11 17:37:20 -0700916 checkExpr.append('const VkStructureType {}[] = {{ {} }};\n'.format(extStructVar, ', '.join([self.structTypes[s] for s in value.extstructs])))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700917 checkExpr.append('skip |= validate_struct_pnext("{}", {ppp}"{}"{pps}, {}, {}{}, {}, {}, GeneratedVulkanHeaderVersion, {});\n'.format(
Mark Lobodzinski3c828522017-06-26 13:05:57 -0600918 funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar, vuid, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600919 return checkExpr
920 #
921 # Generate the pointer check string
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600922 def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName, postProcSpec, struct_type_name):
Mark Lobodzinski85672672016-10-13 08:36:42 -0600923 checkExpr = []
Mark Lobodzinskidead0b62017-06-28 13:22:03 -0600924 vuid_tag_name = struct_type_name if struct_type_name is not None else funcPrintName
Mark Lobodzinski85672672016-10-13 08:36:42 -0600925 if lenValue:
Dave Houlton413a6782018-05-22 13:01:54 -0600926 count_required_vuid = self.GetVuid(vuid_tag_name, "%s-arraylength" % (lenValue.name))
927 array_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600928 # This is an array with a pointer to a count value
929 if lenValue.ispointer:
930 # If count and array parameters are optional, there will be no validation
931 if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true':
932 # 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 -0700933 checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600934 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 -0600935 # This is an array with an integer count value
936 else:
937 # If count and array parameters are optional, there will be no validation
938 if valueRequired == 'true' or lenValueRequired == 'true':
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600939 if value.type != 'char':
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700940 checkExpr.append('skip |= validate_array("{}", {ppp}"{ldn}"{pps}, {ppp}"{dn}"{pps}, {pf}{ln}, &{pf}{vn}, {}, {}, {}, {});\n'.format(
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -0600941 funcPrintName, lenValueRequired, valueRequired, count_required_vuid, array_required_vuid, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix, **postProcSpec))
942 else:
943 # Arrays of strings receive special processing
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700944 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 -0600945 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 -0600946 if checkExpr:
947 if lenValue and ('->' in lenValue.name):
948 # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count
949 checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr)
950 # This is an individual struct that is not allowed to be NULL
951 elif not value.isoptional:
952 # Function pointers need a reinterpret_cast to void*
Dave Houlton413a6782018-05-22 13:01:54 -0600953 ptr_required_vuid = self.GetVuid(vuid_tag_name, "%s-parameter" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600954 if value.type[:4] == 'PFN_':
Dave Houlton413a6782018-05-22 13:01:54 -0600955 allocator_dict = {'pfnAllocation': '"VUID-VkAllocationCallbacks-pfnAllocation-00632"',
956 'pfnReallocation': '"VUID-VkAllocationCallbacks-pfnReallocation-00633"',
957 'pfnFree': '"VUID-VkAllocationCallbacks-pfnFree-00634"',
Mark Lobodzinski02fa1972017-06-28 14:46:14 -0600958 }
959 vuid = allocator_dict.get(value.name)
960 if vuid is not None:
Dave Houlton413a6782018-05-22 13:01:54 -0600961 ptr_required_vuid = vuid
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700962 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 -0600963 else:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -0700964 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 +0100965 else:
966 # Special case for optional internal allocation function pointers.
967 if (value.type, value.name) == ('PFN_vkInternalAllocationNotification', 'pfnInternalAllocation'):
Ricardo Garciac52489a2019-04-02 18:15:20 +0200968 checkExpr.extend(self.internalAllocationCheck(funcPrintName, prefix, value.name, 'pfnInternalFree', postProcSpec))
969 elif (value.type, value.name) == ('PFN_vkInternalFreeNotification', 'pfnInternalFree'):
970 checkExpr.extend(self.internalAllocationCheck(funcPrintName, prefix, value.name, 'pfnInternalAllocation', postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -0600971 return checkExpr
Ricardo Garciac52489a2019-04-02 18:15:20 +0200972
973 #
974 # Generate internal allocation function pointer check.
975 def internalAllocationCheck(self, funcPrintName, prefix, name, complementaryName, postProcSpec):
976 checkExpr = []
977 vuid = '"VUID-VkAllocationCallbacks-pfnInternalAllocation-00635"'
978 checkExpr.append('if ({}{} != NULL)'.format(prefix, name))
979 checkExpr.append('{')
980 local_indent = self.incIndent('')
981 # Function pointers need a reinterpret_cast to void*
982 checkExpr.append(local_indent + 'skip |= validate_required_pointer("{}", {ppp}"{}{}"{pps}, reinterpret_cast<const void*>({}{}), {});\n'.format(funcPrintName, prefix, complementaryName, prefix, complementaryName, vuid, **postProcSpec))
983 checkExpr.append('}\n')
984 return checkExpr
985
Mark Lobodzinski85672672016-10-13 08:36:42 -0600986 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -0600987 # Process struct member validation code, performing name substitution if required
Mark Lobodzinski85672672016-10-13 08:36:42 -0600988 def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec):
989 # Build format specifier list
990 kwargs = {}
991 if '{postProcPrefix}' in line:
992 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
993 if type(memberDisplayNamePrefix) is tuple:
994 kwargs['postProcPrefix'] = 'ParameterName('
995 else:
996 kwargs['postProcPrefix'] = postProcSpec['ppp']
997 if '{postProcSuffix}' in line:
998 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
999 if type(memberDisplayNamePrefix) is tuple:
1000 kwargs['postProcSuffix'] = ', ParameterName::IndexVector{{ {}{} }})'.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
1001 else:
1002 kwargs['postProcSuffix'] = postProcSpec['pps']
1003 if '{postProcInsert}' in line:
1004 # If we have a tuple that includes a format string and format parameters, need to use ParameterName class
1005 if type(memberDisplayNamePrefix) is tuple:
1006 kwargs['postProcInsert'] = '{}{}, '.format(postProcSpec['ppi'], memberDisplayNamePrefix[1])
1007 else:
1008 kwargs['postProcInsert'] = postProcSpec['ppi']
1009 if '{funcName}' in line:
1010 kwargs['funcName'] = funcName
1011 if '{valuePrefix}' in line:
1012 kwargs['valuePrefix'] = memberNamePrefix
1013 if '{displayNamePrefix}' in line:
1014 # Check for a tuple that includes a format string and format parameters to be used with the ParameterName class
1015 if type(memberDisplayNamePrefix) is tuple:
1016 kwargs['displayNamePrefix'] = memberDisplayNamePrefix[0]
1017 else:
1018 kwargs['displayNamePrefix'] = memberDisplayNamePrefix
1019
1020 if kwargs:
1021 # Need to escape the C++ curly braces
1022 if 'IndexVector' in line:
1023 line = line.replace('IndexVector{ ', 'IndexVector{{ ')
1024 line = line.replace(' }),', ' }}),')
1025 return line.format(**kwargs)
1026 return line
1027 #
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001028 # Process struct member validation code, stripping metadata
1029 def ScrubStructCode(self, code):
1030 scrubbed_lines = ''
1031 for line in code:
1032 if 'validate_struct_pnext' in line:
1033 continue
1034 if 'allowed_structs' in line:
1035 continue
1036 if 'xml-driven validation' in line:
1037 continue
1038 line = line.replace('{postProcPrefix}', '')
1039 line = line.replace('{postProcSuffix}', '')
1040 line = line.replace('{postProcInsert}', '')
1041 line = line.replace('{funcName}', '')
1042 line = line.replace('{valuePrefix}', '')
1043 line = line.replace('{displayNamePrefix}', '')
1044 line = line.replace('{IndexVector}', '')
1045 line = line.replace('local_data->', '')
1046 scrubbed_lines += line
1047 return scrubbed_lines
1048 #
Mark Lobodzinski85672672016-10-13 08:36:42 -06001049 # Process struct validation code for inclusion in function or parent struct validation code
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001050 def expandStructCode(self, item_type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output, postProcSpec):
1051 lines = self.validatedStructs[item_type]
Mark Lobodzinski85672672016-10-13 08:36:42 -06001052 for line in lines:
1053 if output:
1054 output[-1] += '\n'
1055 if type(line) is list:
1056 for sub in line:
1057 output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1058 else:
1059 output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix, postProcSpec))
1060 return output
1061 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001062 # Process struct pointer/array validation code, performing name substitution if required
Mark Lobodzinski85672672016-10-13 08:36:42 -06001063 def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName, postProcSpec):
1064 expr = []
1065 expr.append('if ({}{} != NULL)\n'.format(prefix, value.name))
1066 expr.append('{')
1067 indent = self.incIndent(None)
1068 if lenValue:
1069 # Need to process all elements in the array
1070 indexName = lenValue.name.replace('Count', 'Index')
1071 expr[-1] += '\n'
Mark Young39389872017-01-19 21:10:49 -07001072 if lenValue.ispointer:
1073 # If the length value is a pointer, de-reference it for the count.
1074 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < *{}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
1075 else:
1076 expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001077 expr.append(indent + '{')
1078 indent = self.incIndent(indent)
1079 # Prefix for value name to display in error message
Mark Lobodzinski6f82eb52016-12-05 07:38:41 -07001080 if value.ispointer == 2:
1081 memberNamePrefix = '{}{}[{}]->'.format(prefix, value.name, indexName)
1082 memberDisplayNamePrefix = ('{}[%i]->'.format(valueDisplayName), indexName)
1083 else:
1084 memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName)
1085 memberDisplayNamePrefix = ('{}[%i].'.format(valueDisplayName), indexName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001086 else:
1087 memberNamePrefix = '{}{}->'.format(prefix, value.name)
1088 memberDisplayNamePrefix = '{}->'.format(valueDisplayName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001089 # Expand the struct validation lines
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001090 expr = self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr, postProcSpec)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001091 if lenValue:
1092 # Close if and for scopes
1093 indent = self.decIndent(indent)
1094 expr.append(indent + '}\n')
1095 expr.append('}\n')
1096 return expr
1097 #
1098 # Generate the parameter checking code
1099 def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName):
1100 lines = [] # Generated lines of code
1101 unused = [] # Unused variable names
1102 for value in values:
1103 usedLines = []
1104 lenParam = None
1105 #
1106 # 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.
1107 postProcSpec = {}
1108 postProcSpec['ppp'] = '' if not structTypeName else '{postProcPrefix}'
1109 postProcSpec['pps'] = '' if not structTypeName else '{postProcSuffix}'
1110 postProcSpec['ppi'] = '' if not structTypeName else '{postProcInsert}'
1111 #
1112 # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name
1113 valueDisplayName = '{}{}'.format(displayNamePrefix, value.name)
1114 #
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001115 # Check for NULL pointers, ignore the in-out count parameters that
Mark Lobodzinski85672672016-10-13 08:36:42 -06001116 # will be validated with their associated array
1117 if (value.ispointer or value.isstaticarray) and not value.iscount:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001118 # Parameters for function argument generation
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001119 req = 'true' # Parameter cannot be NULL
Mark Lobodzinski85672672016-10-13 08:36:42 -06001120 cpReq = 'true' # Count pointer cannot be NULL
1121 cvReq = 'true' # Count value cannot be 0
1122 lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied
Mark Lobodzinski85672672016-10-13 08:36:42 -06001123 # Generate required/optional parameter strings for the pointer and count values
1124 if value.isoptional:
1125 req = 'false'
1126 if value.len:
1127 # The parameter is an array with an explicit count parameter
1128 lenParam = self.getLenParam(values, value.len)
1129 lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name)
1130 if lenParam.ispointer:
1131 # Count parameters that are pointers are inout
1132 if type(lenParam.isoptional) is list:
1133 if lenParam.isoptional[0]:
1134 cpReq = 'false'
1135 if lenParam.isoptional[1]:
1136 cvReq = 'false'
1137 else:
1138 if lenParam.isoptional:
1139 cpReq = 'false'
1140 else:
1141 if lenParam.isoptional:
1142 cvReq = 'false'
1143 #
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001144 # The parameter will not be processed when tagged as 'noautovalidity'
Mark Lobodzinski85672672016-10-13 08:36:42 -06001145 # For the pointer to struct case, the struct pointer will not be validated, but any
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001146 # members not tagged as 'noautovalidity' will be validated
1147 # We special-case the custom allocator checks, as they are explicit but can be auto-generated.
Ricardo Garcia98ba9642019-02-19 11:25:18 +01001148 AllocatorFunctions = ['PFN_vkAllocationFunction', 'PFN_vkReallocationFunction', 'PFN_vkFreeFunction', 'PFN_vkInternalAllocationNotification', 'PFN_vkInternalFreeNotification']
Mark Lobodzinski899338b2018-07-26 13:59:52 -06001149 if value.noautovalidity and value.type not in AllocatorFunctions:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001150 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1151 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1152 else:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001153 if value.type in self.structTypes:
Mark Lobodzinski87017df2018-05-30 11:29:24 -06001154 # If this is a pointer to a struct with an sType field, verify the type
Mark Lobodzinski9cf24dd2017-06-28 14:23:22 -06001155 usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001156 # 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
1157 elif value.type in self.handleTypes and value.isconst and not self.isHandleOptional(value, lenParam):
1158 usedLines += self.makeHandleCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1159 elif value.type in self.flags and value.isconst:
1160 usedLines += self.makeFlagsArrayCheck(valuePrefix, value, lenParam, req, cvReq, funcName, lenDisplayName, valueDisplayName, postProcSpec)
1161 elif value.isbool and value.isconst:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001162 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 -06001163 elif value.israngedenum and value.isconst:
Mark Lobodzinskiaff801e2017-07-25 15:29:57 -06001164 enum_value_list = 'All%sEnums' % value.type
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001165 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 -07001166 elif value.name == 'pNext' and value.isconst:
Mark Lobodzinski9ddf9282018-05-31 13:59:59 -06001167 usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001168 else:
Mark Lobodzinski1e707bd2017-06-28 10:54:55 -06001169 usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName, postProcSpec, structTypeName)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001170 # 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 -06001171 if value.type in self.validatedStructs:
1172 if value.isconst: # or value.type in self.returnedonly_structs:
1173 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
1174 elif value.type in self.returnedonly_structs:
1175 usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName, postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001176 # Non-pointer types
1177 else:
Mark Lobodzinski85672672016-10-13 08:36:42 -06001178 # The parameter will not be processes when tagged as 'noautovalidity'
1179 # For the struct case, the struct type will not be validated, but any
Mark Lobodzinskia1368552018-05-04 16:03:28 -06001180 # members not tagged as 'noautovalidity' will be validated
Mark Lobodzinski85672672016-10-13 08:36:42 -06001181 if value.noautovalidity:
1182 # Log a diagnostic message when validation cannot be automatically generated and must be implemented manually
1183 self.logMsg('diag', 'ParameterValidation: No validation for {} {}'.format(structTypeName if structTypeName else funcName, value.name))
1184 else:
Mark Lobodzinski024b2822017-06-27 13:22:05 -06001185 vuid_name_tag = structTypeName if structTypeName is not None else funcName
Mark Lobodzinski85672672016-10-13 08:36:42 -06001186 if value.type in self.structTypes:
1187 stype = self.structTypes[value.type]
Dave Houlton413a6782018-05-22 13:01:54 -06001188 vuid = self.GetVuid(value.type, "sType-sType")
Mark Lobodzinskia16ebc72018-06-15 14:47:39 -06001189 undefined_vuid = '"kVUIDUndefined"'
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001190 usedLines.append('skip |= validate_struct_type("{}", {ppp}"{}"{pps}, "{sv}", &({}{vn}), {sv}, false, kVUIDUndefined, {});\n'.format(
Mike Schuchardt24ac4e72018-08-11 17:37:20 -07001191 funcName, valueDisplayName, valuePrefix, vuid, vn=value.name, sv=stype, vt=value.type, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001192 elif value.type in self.handleTypes:
1193 if not self.isHandleOptional(value, None):
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001194 usedLines.append('skip |= validate_required_handle("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001195 elif value.type in self.flags:
1196 flagBitsName = value.type.replace('Flags', 'FlagBits')
1197 if not flagBitsName in self.flagBits:
Dave Houlton413a6782018-05-22 13:01:54 -06001198 vuid = self.GetVuid(vuid_name_tag, "%s-zerobitmask" % (value.name))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001199 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 -06001200 else:
Mark Lobodzinskie643fcc2017-06-26 16:27:15 -06001201 if value.isoptional:
1202 flagsRequired = 'false'
Dave Houlton413a6782018-05-22 13:01:54 -06001203 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinskie643fcc2017-06-26 16:27:15 -06001204 else:
1205 flagsRequired = 'true'
Dave Houlton413a6782018-05-22 13:01:54 -06001206 vuid = self.GetVuid(vuid_name_tag, "%s-requiredbitmask" % (value.name))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001207 allFlagsName = 'All' + flagBitsName
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001208 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 -06001209 elif value.type in self.flagBits:
1210 flagsRequired = 'false' if value.isoptional else 'true'
1211 allFlagsName = 'All' + value.type
Dave Houlton413a6782018-05-22 13:01:54 -06001212 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001213 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 -06001214 elif value.isbool:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001215 usedLines.append('skip |= validate_bool32("{}", {ppp}"{}"{pps}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name, **postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001216 elif value.israngedenum:
Dave Houlton413a6782018-05-22 13:01:54 -06001217 vuid = self.GetVuid(vuid_name_tag, "%s-parameter" % (value.name))
Mark Lobodzinski74cb45f2017-07-25 15:10:29 -06001218 enum_value_list = 'All%sEnums' % value.type
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001219 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 -06001220 # If this is a struct, see if it contains members that need to be checked
1221 if value.type in self.validatedStructs:
1222 memberNamePrefix = '{}{}.'.format(valuePrefix, value.name)
1223 memberDisplayNamePrefix = '{}.'.format(valueDisplayName)
Mark Lobodzinski554cf372018-05-24 11:06:00 -06001224 usedLines.append(self.expandStructCode(value.type, funcName, memberNamePrefix, memberDisplayNamePrefix, '', [], postProcSpec))
Mark Lobodzinski85672672016-10-13 08:36:42 -06001225 # Append the parameter check to the function body for the current command
1226 if usedLines:
1227 # Apply special conditional checks
1228 if value.condition:
1229 usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines)
1230 lines += usedLines
1231 elif not value.iscount:
1232 # If no expression was generated for this value, it is unreferenced by the validation function, unless
1233 # it is an array count, which is indirectly referenced for array valiadation.
1234 unused.append(value.name)
Mark Lobodzinskid4950072017-08-01 13:02:20 -06001235 if not lines:
1236 lines.append('// No xml-driven validation\n')
Mark Lobodzinski85672672016-10-13 08:36:42 -06001237 return lines, unused
1238 #
1239 # Generate the struct member check code from the captured data
1240 def processStructMemberData(self):
1241 indent = self.incIndent(None)
1242 for struct in self.structMembers:
1243 #
1244 # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented
1245 lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name)
1246 if lines:
1247 self.validatedStructs[struct.name] = lines
1248 #
1249 # Generate the command param check code from the captured data
1250 def processCmdData(self):
1251 indent = self.incIndent(None)
1252 for command in self.commands:
1253 # Skip first parameter if it is a dispatch handle (everything except vkCreateInstance)
1254 startIndex = 0 if command.name == 'vkCreateInstance' else 1
1255 lines, unused = self.genFuncBody(command.name, command.params[startIndex:], '', '', None)
Mark Lobodzinski3f10bfe2017-08-23 15:23:23 -06001256 # Cannot validate extension dependencies for device extension APIs having a physical device as their dispatchable object
Mike Schuchardtafd00482017-08-24 15:15:02 -06001257 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 -06001258 ext_test = ''
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001259 if command.params[0].type in ["VkInstance", "VkPhysicalDevice"] or command.name == 'vkCreateInstance':
1260 ext_table_type = 'instance'
1261 else:
1262 ext_table_type = 'device'
Mike Schuchardtafd00482017-08-24 15:15:02 -06001263 for ext in self.required_extensions[command.name]:
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001264 ext_name_define = ''
1265 ext_enable_name = ''
1266 for extension in self.registry.extensions:
1267 if extension.attrib['name'] == ext:
1268 ext_name_define = extension[0][1].get('name')
1269 ext_enable_name = ext_name_define.lower()
1270 ext_enable_name = re.sub('_extension_name', '', ext_enable_name)
1271 break
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001272 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 -06001273 lines.insert(0, ext_test)
Mark Lobodzinski85672672016-10-13 08:36:42 -06001274 if lines:
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001275 func_sig = self.getCmdDef(command) + ' {\n'
1276 func_sig = func_sig.split('VKAPI_CALL vk')[1]
1277 cmdDef = 'bool StatelessValidation::PreCallValidate' + func_sig
Mark Lobodzinskid4950072017-08-01 13:02:20 -06001278 cmdDef += '%sbool skip = false;\n' % indent
Mark Lobodzinski70d416b2019-07-08 15:59:43 -06001279 for line in lines:
1280 if type(line) is list:
1281 for sub in line:
1282 cmdDef += indent + sub
1283 else:
1284 cmdDef += indent + line
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001285 # Insert call to custom-written function if present
1286 if command.name in self.functions_with_manual_checks:
1287 # Generate parameter list for manual fcn and down-chain calls
1288 params_text = ''
1289 for param in command.params:
1290 params_text += '%s, ' % param.name
1291 params_text = params_text[:-2] + ');\n'
1292 cmdDef += ' skip |= manual_PreCallValidate'+ command.name[2:] + '(' + params_text
Mark Lobodzinskiaf7c0382018-12-18 11:55:55 -07001293 cmdDef += '%sreturn skip;\n' % indent
Mark Lobodzinski85672672016-10-13 08:36:42 -06001294 cmdDef += '}\n'
Mark Lobodzinskid8b7e022017-06-01 15:10:13 -06001295 self.validation.append(cmdDef)