diff options
| author | Dustin Graves <dustin@lunarg.com> | 2016-04-14 15:03:31 -0600 |
|---|---|---|
| committer | Dustin Graves <dustin@lunarg.com> | 2016-04-18 11:51:12 -0600 |
| commit | bd690e5f72f4b4780886c9a8dbc1b890929d5d33 (patch) | |
| tree | 24086e2a591217d2544e4aedaa5bf8f86bc0f2f0 | |
| parent | 74fb72a917d223fa3d0e269f351bcd998bb98595 (diff) | |
| download | usermoji-bd690e5f72f4b4780886c9a8dbc1b890929d5d33.tar.xz | |
layers: Improved generated struct validation code
Improvements to the parameter validation code generator, primarily to
eliminate string concatenations performed when processing structs. A
validation function was being produced for each struct, which received a
string specifying the name of the struct parameter. This string was then
concatenated with the names of the struct members. The struct validation
code is no longer in a separate function, and a single string containing
the full names of each struct member (eg. pCreateInfo->pApplicationInfo)
is created at code generation time.
Change-Id: I2800bfbc26247e4e4c6f8c885c0e7c4b8b3776a2
| -rw-r--r-- | generator.py | 195 |
1 files changed, 80 insertions, 115 deletions
diff --git a/generator.py b/generator.py index a96ada8d..f02419fc 100644 --- a/generator.py +++ b/generator.py @@ -2852,7 +2852,7 @@ class ParamCheckerOutputGenerator(OutputGenerator): self.structTypes = dict() # Map of Vulkan struct typename to required VkStructureType self.commands = [] # List of CommandData records for all Vulkan commands self.structMembers = [] # List of StructMemberData records for all Vulkan structs - self.validatedStructs = set() # Set of structs containing members that require validation + self.validatedStructs = dict() # Map of structs type names to generated validation code for that struct type self.enumRanges = dict() # Map of enum name to BEGIN/END range values # Named tuples to store struct and command data self.StructType = namedtuple('StructType', ['name', 'value']) @@ -2922,7 +2922,7 @@ class ParamCheckerOutputGenerator(OutputGenerator): self.structTypes = dict() self.commands = [] self.structMembers = [] - self.validatedStructs = set() + self.validatedStructs = dict() self.enumRanges = dict() def endFeature(self): # C-specific @@ -2935,7 +2935,6 @@ class ParamCheckerOutputGenerator(OutputGenerator): if (self.featureExtraProtect != None): write('#ifdef', self.featureExtraProtect, file=self.outFile) # Generate the struct member checking code from the captured data - self.prepareStructMemberData() self.processStructMemberData() # Generate the command parameter checking code from the captured data self.processCmdData() @@ -3265,15 +3264,15 @@ class ParamCheckerOutputGenerator(OutputGenerator): # This is an array with a pointer to a count value if lenValue.ispointer: # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required - checkExpr.append('skipCall |= validate_struct_type_array(report_data, {}, {ldn}, {dn}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {});\n'.format( + checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", "{ldn}", "{dn}", "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {});\n'.format( funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix)) # This is an array with an integer count value else: - checkExpr.append('skipCall |= validate_struct_type_array(report_data, {}, {ldn}, {dn}, "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {});\n'.format( + checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", "{ldn}", "{dn}", "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {});\n'.format( funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix)) # This is an individual struct else: - checkExpr.append('skipCall |= validate_struct_type(report_data, {}, {}, "{sv}", {}{vn}, {sv}, {});\n'.format( + checkExpr.append('skipCall |= validate_struct_type(report_data, "{}", "{}", "{sv}", {}{vn}, {sv}, {});\n'.format( funcPrintName, valuePrintName, prefix, valueRequired, vn=value.name, sv=stype.value)) return checkExpr # @@ -3290,7 +3289,7 @@ class ParamCheckerOutputGenerator(OutputGenerator): extStructCount = 'ARRAY_SIZE(allowedStructs)' extStructVar = 'allowedStructs' extStructNames = '"' + ', '.join(structs) + '"' - checkExpr.append('skipCall |= validate_struct_pnext(report_data, {}, {}, {}, {}{}, {}, {});\n'.format( + checkExpr.append('skipCall |= validate_struct_pnext(report_data, "{}", "{}", {}, {}{}, {}, {});\n'.format( funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar)) return checkExpr # @@ -3303,16 +3302,16 @@ class ParamCheckerOutputGenerator(OutputGenerator): # If count and array parameters are optional, there will be no validation if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true': # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required - checkExpr.append('skipCall |= validate_array(report_data, {}, {ldn}, {dn}, {pf}{ln}, {pf}{vn}, {}, {}, {});\n'.format( + checkExpr.append('skipCall |= validate_array(report_data, "{}", "{ldn}", "{dn}", {pf}{ln}, {pf}{vn}, {}, {}, {});\n'.format( funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix)) # This is an array with an integer count value else: # If count and array parameters are optional, there will be no validation if valueRequired == 'true' or lenValueRequired == 'true': # Arrays of strings receive special processing - funcName = 'validate_array' if value.type != 'char' else 'validate_string_array' - checkExpr.append('skipCall |= {}(report_data, {}, {ldn}, {dn}, {pf}{ln}, {pf}{vn}, {}, {});\n'.format( - funcName, funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix)) + validationFuncName = 'validate_array' if value.type != 'char' else 'validate_string_array' + checkExpr.append('skipCall |= {}(report_data, "{}", "{ldn}", "{dn}", {pf}{ln}, {pf}{vn}, {}, {});\n'.format( + validationFuncName, funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix)) if checkExpr: if lenValue and ('->' in lenValue.name): # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count @@ -3321,22 +3320,69 @@ class ParamCheckerOutputGenerator(OutputGenerator): elif not value.isoptional: # Function pointers need a reinterpret_cast to void* if value.type[:4] == 'PFN_': - checkExpr.append('skipCall |= validate_required_pointer(report_data, {}, {}, reinterpret_cast<const void*>({}{}));\n'.format(funcPrintName, valuePrintName, prefix, value.name)) + checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", "{}", reinterpret_cast<const void*>({}{}));\n'.format(funcPrintName, valuePrintName, prefix, value.name)) else: - checkExpr.append('skipCall |= validate_required_pointer(report_data, {}, {}, {}{});\n'.format(funcPrintName, valuePrintName, prefix, value.name)) + checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", "{}", {}{});\n'.format(funcPrintName, valuePrintName, prefix, value.name)) return checkExpr # + # Process struct member validation code, performing name suibstitution if required + def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix): + if any(token in line for token in ['{funcName}', '{valuePrefix}', '{displayNamePrefix}']): + return line.format(funcName=funcName, valuePrefix=memberNamePrefix, displayNamePrefix=memberDisplayNamePrefix) + return line + # + # Process struct validation code for inclusion in function or parent struct validation code + def expandStructCode(self, lines, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output): + for line in lines: + if output: + output[-1] += '\n' + if type(line) is list: + for sub in line: + output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix)) + else: + output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix)) + return output + # + # Process struct pointer/array validation code, perfoeming name substitution if required + def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName): + expr = [] + expr.append('if ({}{} != NULL)\n'.format(prefix, value.name)) + expr.append('{') + indent = self.incIndent(None) + if lenValue: + # Need to process all elements in the array + indexName = lenValue.name.replace('Count', 'Index') + expr[-1] += '\n' + expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName)) + expr.append(indent + '{') + indent = self.incIndent(indent) + # Prefix for value name to display in error message + memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName) + memberDisplayNamePrefix = '{}[i].'.format(valueDisplayName) + else: + memberNamePrefix = '{}{}->'.format(prefix, value.name) + memberDisplayNamePrefix = '{}->'.format(valueDisplayName) + # + # Expand the struct validation lines + expr = self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr) + # + if lenValue: + # Close if and for scopes + indent = self.decIndent(indent) + expr.append(indent + '}\n') + expr.append('}\n') + return expr + # # Generate the parameter checking code def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName): lines = [] # Generated lines of code unused = [] # Unused variable names for value in values: - usedAlways = [] - usedOnInput = [] + usedLines = [] lenParam = None # # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name - valueDisplayName = '(std::string({}) + std::string("{}")).c_str()'.format(displayNamePrefix, value.name) if displayNamePrefix else '"{}"'.format(value.name) + valueDisplayName = '{}{}'.format(displayNamePrefix, value.name) # # Check for NULL pointers, ignore the inout count parameters that # will be validated with their associated array @@ -3354,7 +3400,7 @@ class ParamCheckerOutputGenerator(OutputGenerator): if value.len: # The parameter is an array with an explicit count parameter lenParam = self.getLenParam(values, value.len) - lenDisplayName = '(std::string({}) + std::string("{}")).c_str()'.format(displayNamePrefix, lenParam.name) if displayNamePrefix else '"{}"'.format(lenParam.name) + lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name) if lenParam.ispointer: # Count parameters that are pointers are inout if type(lenParam.isoptional) is list: @@ -3371,138 +3417,57 @@ class ParamCheckerOutputGenerator(OutputGenerator): # # If this is a pointer to a struct with an sType field, verify the type if value.type in self.structTypes: - usedAlways += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName) + usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName) elif value.name == 'pNext': # We need to ignore VkDeviceCreateInfo and VkInstanceCreateInfo, as the loader manipulates them in a way that is not documented in vk.xml if not structTypeName in ['VkDeviceCreateInfo', 'VkInstanceCreateInfo']: - usedAlways += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName) + usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName) else: - usedAlways += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName) + usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName) # # If this is a pointer to a struct (input), see if it contains members that need to be checked if value.type in self.validatedStructs and value.isconst: - # - # The name prefix used when reporting an error with a struct member (eg. the 'pCreateInfor->' in 'pCreateInfo->sType') - if lenParam: - prefix = '(std::string({}) + std::string("{}[i]->")).c_str()'.format(displayNamePrefix, value.name) if displayNamePrefix else '(std::string("{}[i]->")).c_str()'.format(value.name) - else: - prefix = '(std::string({}) + std::string("{}->")).c_str()'.format(displayNamePrefix, value.name) if displayNamePrefix else '"{}->"'.format(value.name) - # Validation function does not have an isInput field - if lenParam: - expr = [] - # Need to process all elements in the array - expr.append('if ({}{} != NULL) {{\n'.format(valuePrefix, value.name)) - indent = self.incIndent(None) - expr.append(indent + 'for (uint32_t i = 0; i < {}{}; ++i) {{\n'.format(valuePrefix, lenParam.name)) - indent = self.incIndent(indent) - expr.append(indent + 'skipCall |= parameter_validation_{}(report_data, {}, {}, &({}{}[i]));\n'.format(value.type, funcName, prefix, valuePrefix, value.name)) - indent = self.decIndent(indent) - expr.append(indent + '}\n') - indent = self.decIndent(indent) - expr.append(indent + '}\n') - else: - expr = 'skipCall |= parameter_validation_{}(report_data, {}, {}, {}{});\n'.format(value.type, funcName, prefix, valuePrefix, value.name) - usedAlways.append(expr) + usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName)) elif value.isbool and value.isconst: - usedOnInput.append('skipCall |= validate_bool32_array(report_data, {}, {}, {pf}{}, {pf}{});\n'.format(funcName, valueDisplayName, lenParam.name, value.name, pf=valuePrefix)) + usedLines.append('skipCall |= validate_bool32_array(report_data, "{}", "{}", {pf}{}, {pf}{});\n'.format(funcName, valueDisplayName, lenParam.name, value.name, pf=valuePrefix)) elif value.israngedenum and value.isconst: enumRange = self.enumRanges[value.type] - usedOnInput.append('skipCall |= validate_ranged_enum_array(report_data, {}, {}, "{}", {}, {}, {pf}{}, {pf}{});\n'.format(funcName, valueDisplayName, value.type, enumRange[0], enumRange[1], lenParam.name, value.name, pf=valuePrefix)) + usedLines.append('skipCall |= validate_ranged_enum_array(report_data, "{}", "{}", "{}", {}, {}, {pf}{}, {pf}{});\n'.format(funcName, valueDisplayName, value.type, enumRange[0], enumRange[1], lenParam.name, value.name, pf=valuePrefix)) + # Non-pointer types elif value.type in self.validatedStructs: - # The name of the value with prefix applied - prefix = '(std::string({}) + std::string("{}.")).c_str()'.format(displayNamePrefix, value.name) if displayNamePrefix else '"{}."'.format(value.name) - usedAlways.append('skipCall |= parameter_validation_{}(report_data, {}, {}, &({}{}));\n'.format(value.type, funcName, prefix, valuePrefix, value.name)) + memberNamePrefix = '{}{}.'.format(valuePrefix, value.name) + memberDisplayNamePrefix = '{}.'.format(valueDisplayName) + usedLines.append(self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, '', [])) elif value.isbool: - usedOnInput.append('skipCall |= validate_bool32(report_data, {}, {}, {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name)) + usedLines.append('skipCall |= validate_bool32(report_data, "{}", "{}", {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name)) elif value.israngedenum: enumRange = self.enumRanges[value.type] - usedOnInput.append('skipCall |= validate_ranged_enum(report_data, {}, {}, "{}", {}, {}, {}{});\n'.format(funcName, valueDisplayName, value.type, enumRange[0], enumRange[1], valuePrefix, value.name)) + usedLines.append('skipCall |= validate_ranged_enum(report_data, "{}", "{}", "{}", {}, {}, {}{});\n'.format(funcName, valueDisplayName, value.type, enumRange[0], enumRange[1], valuePrefix, value.name)) # # Append the parameter check to the function body for the current command - if usedAlways or usedOnInput: - # Both used always and used on input are currently treated the same - if usedAlways: - lines += usedAlways - if usedOnInput: - lines += usedOnInput + if usedLines: + lines += usedLines elif not value.iscount: # If no expression was generated for this value, it is unreferenced by the validation function, unless # it is an array count, which is indirectly referenced for array valiadation. unused.append(value.name) return lines, unused # - # Post-process the collected struct member data to create a list of structs - # with members that need to be validated - def prepareStructMemberData(self): - for struct in self.structMembers: - validated = False - for member in struct.members: - # Counts will be validated with their associated array - if not member.iscount: - lenParam = self.getLenParam(struct.members, member.len) - # The sType value needs to be validated - # The pNext value needs to be validated - # A required array/count needs to be validated - # A required pointer needs to be validated - # A bool needs to be validated, and the struct is an input parameter - # An enum needs to be validated, and the struct is an input parameter - if member.type in self.structTypes: - validated = True - elif member.name == 'pNext': - validated = True - elif member.ispointer and lenParam: # This is an array - # Make sure len is not optional - if lenParam.ispointer: - if not lenParam.isoptional[0] or not lenParam.isoptional[1] or not member.isoptional: - validated = True - else: - if not lenParam.isoptional or not member.isoptional: - validated = True - elif member.ispointer and not member.isoptional: - validated = True - elif member.isbool or member.israngedenum: - validated = True - # - if validated: - self.validatedStructs.add(struct.name) - # # Generate the struct member check code from the captured data def processStructMemberData(self): indent = self.incIndent(None) for struct in self.structMembers: # # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented - lines, unused = self.genFuncBody('pFuncName', struct.members, 'pStruct->', 'pVariableName', struct.name) + lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name) if lines: - cmdDef = 'static bool parameter_validation_{}(\n'.format(struct.name) - cmdDef += ' debug_report_data*'.ljust(self.genOpts.alignFuncParam) + ' report_data,\n' - cmdDef += ' const char*'.ljust(self.genOpts.alignFuncParam) + ' pFuncName,\n' - cmdDef += ' const char*'.ljust(self.genOpts.alignFuncParam) + ' pVariableName,\n' - cmdDef += ' const {}*'.format(struct.name).ljust(self.genOpts.alignFuncParam) + ' pStruct)\n' - cmdDef += '{\n' - cmdDef += indent + 'bool skipCall = false;\n' - cmdDef += '\n' - cmdDef += indent + 'if (pStruct != NULL) {' - indent = self.incIndent(indent) - for line in lines: - cmdDef += '\n' - if type(line) is list: - for sub in line: - cmdDef += indent + sub - else: - cmdDef += indent + line - indent = self.decIndent(indent) - cmdDef += indent +'}\n' - cmdDef += '\n' - cmdDef += indent + 'return skipCall;\n' - cmdDef += '}\n' - self.appendSection('command', cmdDef) + self.validatedStructs[struct.name] = lines # # Generate the command param check code from the captured data def processCmdData(self): indent = self.incIndent(None) for command in self.commands: - lines, unused = self.genFuncBody('"{}"'.format(command.name), command.params, '', None, None) + lines, unused = self.genFuncBody(command.name, command.params, '', '', None) if lines: cmdDef = self.getCmdDef(command) + '\n' cmdDef += '{\n' |
