From 6be84edcd971c6663d63a51cf07dbbf8ce7bcfe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20S=C3=BC=C3=9Fenbach?= Date: Thu, 7 Dec 2017 08:46:58 +0100 Subject: [PATCH] Improve error handling by validating attributes and give more meaningful error messages --- VulkanHppGenerator.cpp | 420 ++++++++++++++++++++++++++++------------- 1 file changed, 294 insertions(+), 126 deletions(-) diff --git a/VulkanHppGenerator.cpp b/VulkanHppGenerator.cpp index d1149a9..b8759d3 100644 --- a/VulkanHppGenerator.cpp +++ b/VulkanHppGenerator.cpp @@ -877,7 +877,7 @@ struct VendorIDData struct VkData { - std::map aliases; // Enum Aliases + std::map aliases; std::map commands; std::map constants; std::set defines; @@ -903,6 +903,7 @@ struct VkData }; void aliasType(VkData & vkData, DependencyData::Category category, std::string const& aliasName, std::string const& newName, std::string const& protect); +void checkAttributes(std::map const& attributes, int line, std::map> const& required, std::map> const& optional); void createDefaults( VkData const& vkData, std::map & defaultValues ); void determineEnhancedReturnType(CommandData & commandData); void determineReducedName(CommandData & commandData); @@ -918,6 +919,7 @@ bool hasPointerParam(std::vector const& params); void leaveProtect(std::ostream &os, std::string const& protect); void linkCommandToHandle(VkData & vkData, CommandData & commandData); std::string readArraySize(tinyxml2::XMLNode * node, std::string& name); +std::map readAttributes(tinyxml2::XMLElement* element); bool readCommandParam( tinyxml2::XMLElement * element, std::set & dependencies, std::vector & params ); void readCommandParams(tinyxml2::XMLElement* element, std::set & dependencies, CommandData & commandData); tinyxml2::XMLNode* readCommandParamType(tinyxml2::XMLNode* node, ParamData& param); @@ -940,13 +942,12 @@ void readExtensionType(tinyxml2::XMLElement * element, VkData & vkData, std::str void readFeature(tinyxml2::XMLElement * element, std::map & enums, std::map & nameMap); void readFeatureRequire(tinyxml2::XMLElement * element, std::map & enums, std::map & nameMap); void readFeatureRequireEnum(tinyxml2::XMLElement * element, std::map & enums, std::map & nameMap); -tinyxml2::XMLNode* readType(tinyxml2::XMLNode* element, std::string & type, std::string & pureType); void readTypeBasetype(tinyxml2::XMLElement * element, std::list & dependencies, std::map const& attributes); void readTypeBitmask(tinyxml2::XMLElement * element, VkData & vkData, std::map const& attributes); void readTypeDefine(tinyxml2::XMLElement * element, VkData & vkData, std::map const& attributes); -void readTypeFuncpointer( tinyxml2::XMLElement * element, std::list & dependencies ); +void readTypeFuncpointer( tinyxml2::XMLElement * element, std::list & dependencies, std::map const& attributes); void readTypeHandle(tinyxml2::XMLElement * element, VkData & vkData, std::map const& attributes); -void readTypeStruct( tinyxml2::XMLElement * element, VkData & vkData, bool isUnion ); +void readTypeStruct( tinyxml2::XMLElement * element, VkData & vkData, bool isUnion, std::map const& attributes); void readTypeStructMember( tinyxml2::XMLElement * element, VkData & vkData, StructData & structData ); void readTag(tinyxml2::XMLElement * element, std::set & tags); void readTags(tinyxml2::XMLElement * element, std::set & tags); @@ -1015,6 +1016,7 @@ void writeTypes(std::ostream & os, VkData const& vkData, std::map const& attributes); void skipTypeInclude(tinyxml2::XMLElement * element, std::map const& attributes); void skipVendorID(tinyxml2::XMLElement * element, std::vector & vendorIDs); @@ -1084,6 +1086,59 @@ static void setDefault(std::string const& name, std::map const& attributes, int line, std::map> const& required, std::map> const& optional) +{ + std::stringstream ss; + ss << line; + std::string lineNumber = ss.str(); + + // check if all required attributes are included and if there is a set of allowed values, check if the actual value is part of that set + for (auto const& r : required) + { + auto attributesIt = attributes.find(r.first); + if (attributesIt == attributes.end()) + { + assert(false); + throw std::runtime_error("Spec error on line " + lineNumber + ": missing attribute <" + r.first + ">"); + } + if (!r.second.empty() && (r.second.find(attributesIt->second) == r.second.end())) + { + assert(false); + throw std::runtime_error("Spec error on line " + lineNumber + ": unexpected attribute value <" + attributesIt->second + "> in attribute <" + r.first + ">"); + } + } + // check if all not required attributes or optional, and if there is a set of allowed values, check if the actual value is part of that set + for (auto const& a : attributes) + { + if (required.find(a.first) == required.end()) + { + auto optionalIt = optional.find(a.first); + if (optionalIt == optional.end()) + { + assert(false); + throw std::runtime_error("Spec error on line " + lineNumber + ": unexpected attribute <" + a.first + ">"); + } + if (!optionalIt->second.empty()) + { + std::vector values = tokenize(a.second, ','); + for (auto const& v : values) + { + if (optionalIt->second.find(v) == optionalIt->second.end()) + { + assert(false); + throw std::runtime_error("Spec error on line " + lineNumber + ": unexpected attribute value <" + v + "> in attribute <" + a.first + ">"); + } + } + } + } + } +} + void createDefaults( VkData const& vkData, std::map & defaultValues ) { for (auto dependency : vkData.dependencies) @@ -1388,6 +1443,7 @@ std::string readArraySize(tinyxml2::XMLNode * node, std::string& name) node = node->NextSibling(); if (node && node->ToText()) { + assert(node->Value()); std::string value = trimEnd(node->Value()); if (value == "[") { @@ -1410,14 +1466,29 @@ std::string readArraySize(tinyxml2::XMLNode * node, std::string& name) return arraySize; } +std::map readAttributes(tinyxml2::XMLElement* element) +{ + std::map attributes; + for (auto attribute = element->FirstAttribute(); attribute; attribute = attribute->Next()) + { + assert(attributes.find(attribute->Name()) == attributes.end()); + attributes[attribute->Name()] = attribute->Value(); + } + return attributes; +} + bool readCommandParam( tinyxml2::XMLElement * element, std::set & dependencies, std::vector & params ) { + std::map attributes = readAttributes(element); + checkAttributes(attributes, element->GetLineNum(), {}, { {"externsync", {}}, {"len", {}}, {"noautovalidity", {"true"}}, {"optional", {"false", "true"}} }); + ParamData param; bool isTwoStep = false; - if (element->Attribute("len")) + auto lenAttribute = attributes.find("len"); + if (lenAttribute != attributes.end()) { - param.len = element->Attribute("len"); + param.len = lenAttribute->second; auto pit = std::find_if(params.begin(), params.end(), [¶m](ParamData const& pd) { return param.len == pd.name; }); if (pit != params.end()) { @@ -1429,12 +1500,13 @@ bool readCommandParam( tinyxml2::XMLElement * element, std::set & d tinyxml2::XMLNode * child = readCommandParamType(element->FirstChild(), param); dependencies.insert(param.pureType); - assert( child->ToElement() && ( strcmp( child->Value(), "name" ) == 0 ) ); + assert( child->ToElement() && ( strcmp( child->Value(), "name" ) == 0 ) && !child->ToElement()->FirstAttribute() ); param.name = child->ToElement()->GetText(); param.arraySize = readArraySize(child, param.name); - param.optional = element->Attribute("optional") && (strcmp(element->Attribute("optional"), "true") == 0); + auto optionalAttribute = attributes.find("optional"); + param.optional = (optionalAttribute != attributes.end()) && (optionalAttribute->second == "true"); params.push_back(param); @@ -1445,8 +1517,7 @@ bool readCommandParam( tinyxml2::XMLElement * element, std::set & d void readCommandParams(tinyxml2::XMLElement* element, std::set & dependencies, CommandData & commandData) { // iterate over the siblings of the element and read the command parameters - assert(element); - while (element = element->NextSiblingElement()) + for ( ; element ; element = element->NextSiblingElement()) { std::string value = element->Value(); if (value == "param") @@ -1456,7 +1527,7 @@ void readCommandParams(tinyxml2::XMLElement* element, std::set & de else { // ignore these values! - assert((value == "implicitexternsyncparams") || (value == "validity")); + assert(value == "implicitexternsyncparams"); } } } @@ -1475,7 +1546,7 @@ tinyxml2::XMLNode* readCommandParamType(tinyxml2::XMLNode* node, ParamData& para } // get the pure type - assert(node->ToElement() && (strcmp(node->Value(), "type") == 0) && node->ToElement()->GetText()); + assert(node->ToElement() && (strcmp(node->Value(), "type") == 0) && node->ToElement()->GetText() && !node->ToElement()->FirstAttribute()); std::string type = strip(node->ToElement()->GetText(), "Vk"); param.type += type; param.pureType = type; @@ -1496,6 +1567,8 @@ tinyxml2::XMLNode* readCommandParamType(tinyxml2::XMLNode* node, ParamData& para CommandData& readCommandProto(tinyxml2::XMLElement * element, VkData & vkData) { + assert(!element->FirstAttribute()); + tinyxml2::XMLElement * typeElement = element->FirstChildElement(); assert( typeElement && ( strcmp( typeElement->Value(), "type" ) == 0 ) ); tinyxml2::XMLElement * nameElement = typeElement->NextSiblingElement(); @@ -1516,6 +1589,9 @@ CommandData& readCommandProto(tinyxml2::XMLElement * element, VkData & vkData) void readCommands(tinyxml2::XMLElement * element, VkData & vkData) { + std::map attributes = readAttributes(element); + checkAttributes(attributes, element->GetLineNum(), {}, { { "comment", {} } }); + for (tinyxml2::XMLElement* child = element->FirstChildElement(); child; child = child->NextSiblingElement()) { assert(strcmp(child->Value(), "command") == 0); @@ -1525,12 +1601,23 @@ void readCommands(tinyxml2::XMLElement * element, VkData & vkData) void readCommandsCommand(tinyxml2::XMLElement * element, VkData & vkData) { + std::map attributes = readAttributes(element); + checkAttributes(attributes, element->GetLineNum(), {}, + { { "cmdbufferlevel", { "primary", "secondary" } }, + { "comment", {} }, + { "errorcodes", {} }, + { "pipeline", { "compute", "graphics", "transfer" } }, + { "queues", { "compute", "graphics", "sparse_binding", "transfer" } }, + { "renderpass", { "both", "inside", "outside" } }, + { "successcodes", {} } + }); + tinyxml2::XMLElement * child = element->FirstChildElement(); assert( child && ( strcmp( child->Value(), "proto" ) == 0 ) ); CommandData& commandData = readCommandProto(child, vkData); commandData.successCodes = readCommandSuccessCodes(element, vkData.tags); - readCommandParams(child, vkData.dependencies.back().dependencies, commandData); + readCommandParams(child->NextSiblingElement(), vkData.dependencies.back().dependencies, commandData); determineReducedName(commandData); linkCommandToHandle(vkData, commandData); registerDeleter(vkData, commandData); @@ -1588,11 +1675,10 @@ void readComment(tinyxml2::XMLElement * element, std::string & header) void readEnums( tinyxml2::XMLElement * element, VkData & vkData ) { - if (!element->Attribute("name")) - { - throw std::runtime_error(std::string("spec error: enums element is missing the name attribute")); - } - std::string name = strip(element->Attribute("name"), "Vk"); + std::map attributes = readAttributes(element); + checkAttributes(attributes, element->GetLineNum(), { {"name", {}} }, { { "comment", {} }, {"type",{ "bitmask", "enum" } } }); + + std::string name = strip(attributes.find("name")->second, "Vk"); if (name == "API Constants") { @@ -1603,6 +1689,8 @@ void readEnums( tinyxml2::XMLElement * element, VkData & vkData ) } else { + checkAttributes(attributes, element->GetLineNum(), { { "name", {} },{ "type", {"bitmask", "enum"} } }, { { "comment", {} } }); // re-check with type as required + // add an empty DependencyData on this name into the dependencies list vkData.dependencies.push_back( DependencyData( DependencyData::Category::ENUM, name ) ); @@ -1616,17 +1704,7 @@ void readEnums( tinyxml2::XMLElement * element, VkData & vkData ) } else { - if (!element->Attribute("type")) - { - throw std::runtime_error(std::string("spec error: enums name=\"" + name + "\" is missing the type attribute")); - } - std::string type = element->Attribute("type"); - - if (type != "bitmask" && type != "enum") - { - throw std::runtime_error(std::string("spec error: enums name=\"" + name + "\" has unknown type " + type)); - } - + std::string type = attributes.find("type")->second; it->second.bitmask = (type == "bitmask"); if (it->second.bitmask) { @@ -1691,15 +1769,21 @@ void readEnumsEnum( tinyxml2::XMLElement * element, EnumData & enumData, std::ma void readDisabledExtensionRequire(tinyxml2::XMLElement * element, VkData & vkData) { + assert(!element->FirstAttribute()); + for (tinyxml2::XMLElement * child = element->FirstChildElement(); child; child = child->NextSiblingElement()) { + assert(!child->FirstChildElement()); std::string value = child->Value(); if ((value == "command") || (value == "type")) { + std::map attributes = readAttributes(child); + checkAttributes(attributes, element->GetLineNum(), { {"name", {}} }, {}); + // disable a command or a type ! - assert(child->Attribute("name")); - std::string name = (value == "command") ? startLowerCase(strip(child->Attribute("name"), "vk")) : strip(child->Attribute("name"), "Vk"); + auto nameAttribute = attributes.find("name"); + std::string name = (value == "command") ? startLowerCase(strip(nameAttribute->second, "vk")) : strip(nameAttribute->second, "Vk"); // search this name in the dependencies list and remove it std::list::const_iterator depIt = std::find_if(vkData.dependencies.begin(), vkData.dependencies.end(), [&name](DependencyData const& dd) { return(dd.name == name); }); @@ -1737,6 +1821,8 @@ void readDisabledExtensionRequire(tinyxml2::XMLElement * element, VkData & vkDat } else { + std::map attributes = readAttributes(child); + checkAttributes(attributes, child->GetLineNum(), { { "name",{} } }, { {"extends", {}}, {"offset", {}}, { "value",{} } }); // nothing to do for enums, no other values ever encountered assert(value == "enum"); } @@ -1745,9 +1831,12 @@ void readDisabledExtensionRequire(tinyxml2::XMLElement * element, VkData & vkDat void readExtensionAlias(tinyxml2::XMLElement * element, VkData & vkData, std::string const& protect, std::string const& tag) { - assert(element->Attribute("name") && element->Attribute("value")); - std::string name = element->Attribute("name"); - std::string value = element->Attribute("value"); + std::map attributes = readAttributes(element); + checkAttributes(attributes, element->GetLineNum(), { { "name",{} }, {"value", {}} }, {}); + assert(!element->FirstChildElement()); + + std::string name = attributes.find("name")->second; + std::string value = attributes.find("value")->second; auto commandsIt = vkData.commands.find(startLowerCase(strip(value, "vk"))); if (commandsIt != vkData.commands.end()) @@ -1851,11 +1940,14 @@ void readExtensionAlias(tinyxml2::XMLElement * element, VkData & vkData, std::st void readExtensionCommand(tinyxml2::XMLElement * element, std::map & commands, std::string const& protect) { + std::map attributes = readAttributes(element); + checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, {}); + assert(!element->FirstChildElement()); + // just add the protect string to the CommandData if (!protect.empty()) { - assert(element->Attribute("name")); - std::string name = startLowerCase(strip(element->Attribute("name"), "vk")); + std::string name = startLowerCase(strip(attributes.find("name")->second, "vk")); std::map::iterator cit = commands.find(name); assert(cit != commands.end()); cit->second.protect = protect; @@ -1864,21 +1956,28 @@ void readExtensionCommand(tinyxml2::XMLElement * element, std::map & enums, std::string const& tag, std::map & nameMap) { + std::map attributes = readAttributes(element); + checkAttributes(attributes, element->GetLineNum(), { {"name", {}} }, { {"bitpos", {}}, {"comment", {}}, {"dir", {"-"}}, {"extends", {}}, {"offset", {}}, { "value",{} } }); + assert(!element->FirstChildElement()); + // TODO process enums which don't extend existing enums - if (element->Attribute("extends")) + auto extendsAttribute = attributes.find("extends"); + if (extendsAttribute != attributes.end()) { - assert(element->Attribute("name")); - std::string extends = strip(element->Attribute("extends"), "Vk"); + std::string extends = strip(extendsAttribute->second, "Vk"); assert(enums.find(extends) != enums.end()); - assert(!!element->Attribute("bitpos") + !!element->Attribute("offset") + !!element->Attribute("value") == 1); + assert((attributes.find("bitpos") != attributes.end()) + (attributes.find("offset") != attributes.end()) + (attributes.find("value") != attributes.end()) == 1); auto enumIt = enums.find(extends); assert(enumIt != enums.end()); - enumIt->second.addEnumValue(element->Attribute("name"), tag, nameMap); + enumIt->second.addEnumValue(attributes.find("name")->second, tag, nameMap); } } void readExtensionRequire(tinyxml2::XMLElement * element, VkData & vkData, std::string const& protect, std::string const& tag) { + std::map attributes = readAttributes(element); + checkAttributes(attributes, element->GetLineNum(), {}, { {"extension", {}}, {"feature", {}} }); + for (tinyxml2::XMLElement * child = element->FirstChildElement(); child; child = child->NextSiblingElement()) { std::string value = child->Value(); @@ -1899,15 +1998,23 @@ void readExtensionRequire(tinyxml2::XMLElement * element, VkData & vkData, std:: { readExtensionEnum(child, vkData.enums, tag, vkData.nameMap); } +#if !defined(NDEBUG) else { - assert((value == "comment") || (value=="usage")); + assert(value == "comment"); + std::map attributes = readAttributes(child); + checkAttributes(attributes, child->GetLineNum(), {}, {} ); + assert(!child->FirstChildElement()); } +#endif } } void readExtensions(tinyxml2::XMLElement * element, VkData & vkData) { + std::map attributes = readAttributes(element); + checkAttributes(attributes, element->GetLineNum(), { { "comment",{} } }, {}); + for (tinyxml2::XMLElement * child = element->FirstChildElement(); child; child = child->NextSiblingElement()) { assert( strcmp( child->Value(), "extension" ) == 0 ); @@ -1917,7 +2024,23 @@ void readExtensions(tinyxml2::XMLElement * element, VkData & vkData) void readExtensionsExtension(tinyxml2::XMLElement * element, VkData & vkData) { - if (strcmp(element->Attribute("supported"), "disabled") == 0) + std::map attributes = readAttributes(element); + checkAttributes(attributes, element->GetLineNum(), + { + { "name", {} }, + { "number", {} }, + { "supported", { "disabled", "vulkan" } } + }, + { + { "author", {} }, + { "contact", {} }, + { "platform", {} }, + { "protect", {} }, + { "requires", {} }, + { "type", { "device", "instance" } } + }); + + if (attributes.find("supported")->second == "disabled") { // kick out all the disabled stuff we've read before !! for (tinyxml2::XMLElement * child = element->FirstChildElement(); child; child = child->NextSiblingElement()) @@ -1928,30 +2051,32 @@ void readExtensionsExtension(tinyxml2::XMLElement * element, VkData & vkData) } else { - assert( element->Attribute( "name" ) ); - std::string name = element->Attribute("name"); - + std::string name = attributes.find("name")->second; std::string tag = extractTag(name); assert(vkData.tags.find(tag) != vkData.tags.end()); + auto protectAttribute = attributes.find("protect"); + auto platformAttribute = attributes.find("platform"); std::string protect; - if (element->Attribute("protect")) + if (protectAttribute != attributes.end()) { - protect = element->Attribute("protect"); + protect = protectAttribute->second; } - else if (element->Attribute("platform")) + else if (platformAttribute != attributes.end()) { - assert(element->Attribute("author")); - protect = "VK_USE_PLATFORM_" + toUpperCase(element->Attribute("platform")) + "_" + element->Attribute("author"); + auto authorAttribute = attributes.find("author"); + assert(authorAttribute != attributes.end()); + protect = "VK_USE_PLATFORM_" + toUpperCase(platformAttribute->second) + "_" + authorAttribute->second; } #if !defined(NDEBUG) assert(vkData.extensions.find(name) == vkData.extensions.end()); ExtensionData & extension = vkData.extensions.insert(std::make_pair(name, ExtensionData())).first->second; extension.protect = protect; - if (element->Attribute("requires")) + auto requiresAttribute = attributes.find("requires"); + if (requiresAttribute != attributes.end()) { - extension.requires = tokenize(element->Attribute("requires"), ','); + extension.requires = tokenize(requiresAttribute->second, ','); } #endif @@ -1965,11 +2090,14 @@ void readExtensionsExtension(tinyxml2::XMLElement * element, VkData & vkData) void readExtensionType(tinyxml2::XMLElement * element, VkData & vkData, std::string const& protect) { + std::map attributes = readAttributes(element); + checkAttributes(attributes, element->GetLineNum(), {{ "name",{} } }, {}); + assert(!element->FirstChildElement()); + // add the protect-string to the appropriate type: enum, flag, handle, scalar, or struct if (!protect.empty()) { - assert(element->Attribute("name")); - std::string name = strip(element->Attribute("name"), "Vk"); + std::string name = strip(attributes.find("name")->second, "Vk"); std::map::iterator eit = vkData.enums.find(name); if (eit != vkData.enums.end()) { @@ -2025,7 +2153,8 @@ void readExtensionType(tinyxml2::XMLElement * element, VkData & vkData, std::str void readFeature(tinyxml2::XMLElement * element, std::map & enums, std::map & nameMap) { - assert(element->Attribute("api") && (strcmp(element->Attribute("api"), "vulkan") == 0)); + std::map attributes = readAttributes(element); + checkAttributes(attributes, element->GetLineNum(), { {"api", {"vulkan"}},{ "comment",{} }, {"name", {}}, {"number", {}} }, {}); for (tinyxml2::XMLElement * child = element->FirstChildElement(); child; child = child->NextSiblingElement()) { @@ -2038,6 +2167,9 @@ void readFeature(tinyxml2::XMLElement * element, std::map void readFeatureRequire(tinyxml2::XMLElement * element, std::map & enums, std::map & nameMap) { + std::map attributes = readAttributes(element); + checkAttributes(attributes, element->GetLineNum(), {}, { { "comment", {} } }); + for (tinyxml2::XMLElement * child = element->FirstChildElement(); child; child = child->NextSiblingElement()) { assert(child->Value()); @@ -2046,57 +2178,35 @@ void readFeatureRequire(tinyxml2::XMLElement * element, std::map & enums, std::map & nameMap) { - if (element->Attribute("extends")) + std::map attributes = readAttributes(element); + checkAttributes(attributes, element->GetLineNum(), { { "name", {} } }, { {"bitpos", {} }, {"comment", {}}, { "extends",{} },{ "value",{} } }); + + auto extendsAttribute = attributes.find("extends"); + if (extendsAttribute != attributes.end()) { - assert((strncmp(element->Attribute("extends"), "Vk", 2) == 0) && element->Attribute("name")); - std::string extends = strip(element->Attribute("extends"), "Vk"); + assert(strncmp(extendsAttribute->second.c_str(), "Vk", 2) == 0); + std::string extends = strip(extendsAttribute->second, "Vk"); auto enumIt = enums.find(extends); assert(enumIt != enums.end()); - enumIt->second.addEnumValue(element->Attribute("name"), "", nameMap); + enumIt->second.addEnumValue(attributes.find("name")->second, "", nameMap); } } -tinyxml2::XMLNode* readType(tinyxml2::XMLNode* element, std::string & type, std::string & pureType) -{ - assert(element); - if (element->ToText()) - { - std::string value = trim(element->Value()); - assert((value == "const") || (value == "struct")); - type = value + " "; - element = element->NextSibling(); - assert(element); - } - - assert(element->ToElement()); - assert((strcmp(element->Value(), "type") == 0) && element->ToElement() && element->ToElement()->GetText()); - pureType = strip(element->ToElement()->GetText(), "Vk"); - type += pureType; - - element = element->NextSibling(); - assert(element); - if (element->ToText()) - { - std::string value = trimEnd(element->Value()); - assert((value == "*") || (value == "**") || (value == "* const*")); - type += value; - element = element->NextSibling(); - } - return element; -} - void readTypeBasetype(tinyxml2::XMLElement * element, std::list & dependencies, std::map const& attributes) { - assert((attributes.size() == 1) && (attributes.find("category") != attributes.end()) && (attributes.find("category")->second == "basetype")); + checkAttributes(attributes, element->GetLineNum(), { {"category", {"basetype"}} }, {}); tinyxml2::XMLElement * typeElement = element->FirstChildElement(); assert(typeElement && !typeElement->FirstAttribute() && (strcmp(typeElement->Value(), "type") == 0) && typeElement->GetText()); @@ -2123,8 +2233,7 @@ void readTypeBasetype(tinyxml2::XMLElement * element, std::list void readTypeBitmask(tinyxml2::XMLElement * element, VkData & vkData, std::map const& attributes) { - assert((attributes.find("category") != attributes.end()) && (attributes.find("category")->second == "bitmask")); - assert((attributes.size() == 1) || ((attributes.size() == 2) && (attributes.find("requires") != attributes.end()))); + checkAttributes(attributes, element->GetLineNum(), { {"category", {"bitmask"}} }, { {"requires", {}} }); tinyxml2::XMLElement * typeElement = element->FirstChildElement(); assert(typeElement && !typeElement->FirstAttribute() && (strcmp(typeElement->Value(), "type") == 0) && typeElement->GetText() && (strcmp(typeElement->GetText(), "VkFlags") == 0)); @@ -2163,8 +2272,7 @@ void readTypeBitmask(tinyxml2::XMLElement * element, VkData & vkData, std::map const& attributes) { - assert((attributes.find("category") != attributes.end()) && (attributes.find("category")->second == "define")); - assert((attributes.size() == 1) || ((attributes.size() == 2) && (attributes.find("name") != attributes.end()))); + checkAttributes(attributes, element->GetLineNum(), { {"category", {"define"}} }, { {"name", {}} }); auto nameIt = attributes.find("name"); if (nameIt != attributes.end()) @@ -2199,18 +2307,26 @@ void readTypeDefine(tinyxml2::XMLElement * element, VkData & vkData, std::map & dependencies ) +void readTypeFuncpointer( tinyxml2::XMLElement * element, std::list & dependencies, std::map const& attributes) { - tinyxml2::XMLElement * child = element->FirstChildElement(); - assert( child && ( strcmp( child->Value(), "name" ) == 0 ) && child->GetText() ); + checkAttributes(attributes, element->GetLineNum(), { {"category", {"funcpointer"}} }, { {"requires", {}} }); - dependencies.push_back( DependencyData( DependencyData::Category::FUNC_POINTER, child->GetText() ) ); + tinyxml2::XMLElement * nameElement = element->FirstChildElement(); + assert(nameElement && ( strcmp(nameElement->Value(), "name" ) == 0 ) && nameElement->GetText() ); + + dependencies.push_back(DependencyData(DependencyData::Category::FUNC_POINTER, nameElement->GetText())); + +#if !defined(NDEBUG) + for (tinyxml2::XMLElement * typeElement = nameElement->NextSiblingElement(); typeElement; typeElement = typeElement->NextSiblingElement()) + { + assert((strcmp(typeElement->Value(), "type") == 0) && !typeElement->FirstAttribute()); + } +#endif } void readTypeHandle(tinyxml2::XMLElement * element, VkData & vkData, std::map const& attributes) { - assert((attributes.find("category") != attributes.end()) && (attributes.find("category")->second == "handle")); - assert((attributes.size() == 1) || ((attributes.size() == 2) && (attributes.find("parent") != attributes.end()))); + checkAttributes(attributes, element->GetLineNum(), { {"category", {"handle"}} }, { {"parent", {}} }); tinyxml2::XMLElement * typeElement = element->FirstChildElement(); assert(typeElement && !typeElement->FirstAttribute() && (strcmp(typeElement->Value(), "type") == 0) && typeElement->GetText()); @@ -2223,6 +2339,8 @@ void readTypeHandle(tinyxml2::XMLElement * element, VkData & vkData, std::mapFirstAttribute() && (strcmp(nameElement->Value(), "name") == 0) && nameElement->GetText()); std::string name = strip( nameElement->GetText(), "Vk" ); + assert(!nameElement->NextSiblingElement()); + vkData.dependencies.push_back( DependencyData( DependencyData::Category::HANDLE, name ) ); assert(vkData.vkTypes.find(name) == vkData.vkTypes.end()); vkData.vkTypes.insert(name); @@ -2230,23 +2348,32 @@ void readTypeHandle(tinyxml2::XMLElement * element, VkData & vkData, std::map const& attributes) { - assert( !element->Attribute( "returnedonly" ) || ( strcmp( element->Attribute( "returnedonly" ), "true" ) == 0 ) ); + checkAttributes(attributes, element->GetLineNum(), + { + { "category", { isUnion ? "union" : "struct" } }, + { "name", {} } + }, + { + { "comment", {} }, + { "returnedonly", { "true" } }, + { "structextends", {} } + }); - assert( element->Attribute( "name" ) ); - std::string name = strip( element->Attribute( "name" ), "Vk" ); + std::string name = strip( attributes.find("name")->second, "Vk" ); vkData.dependencies.push_back( DependencyData( isUnion ? DependencyData::Category::UNION : DependencyData::Category::STRUCT, name ) ); assert( vkData.structs.find( name ) == vkData.structs.end() ); std::map::iterator it = vkData.structs.insert( std::make_pair( name, StructData() ) ).first; - it->second.returnedOnly = !!element->Attribute( "returnedonly" ); + it->second.returnedOnly = (attributes.find("returnedonly") != attributes.end()); it->second.isUnion = isUnion; - if (element->Attribute("structextends")) + auto attributesIt = attributes.find("structextends"); + if (attributesIt != attributes.end()) { - std::vector structExtends = tokenize(element->Attribute("structextends"), ','); + std::vector structExtends = tokenize(attributesIt->second, ','); for (auto const& s : structExtends) { assert(s.substr(0, 2) == "Vk"); @@ -2274,22 +2401,61 @@ void readTypeStruct( tinyxml2::XMLElement * element, VkData & vkData, bool isUni void readTypeStructMember(tinyxml2::XMLElement * element, VkData & vkData, StructData & structData) { + std::map attributes = readAttributes(element); + checkAttributes(attributes, element->GetLineNum(), {}, + { + { "altlen", {} }, + { "externsync", { "true" } }, + { "len", {} }, + { "noautovalidity", { "true" } }, + { "optional", { "false", "true" } }, + { "values", {} } + }); + structData.members.push_back(MemberData()); MemberData & member = structData.members.back(); - char const* values = element->Attribute("values"); - if (values) + auto valuesAttribute = attributes.find("values"); + if (valuesAttribute != attributes.end()) { - member.values = values; + member.values = valuesAttribute->second; + } + + tinyxml2::XMLNode* child = element->FirstChild(); + assert(child); + if (child->ToText()) + { + std::string value = trim(child->Value()); + assert((value == "const") || (value == "struct")); + member.type = value + " "; + child = child->NextSibling(); + assert(child); + } + + assert(child->ToElement()); + tinyxml2::XMLElement* typeElement = child->ToElement(); + assert((strcmp(typeElement->Value(), "type") == 0) && typeElement->GetText() && !typeElement->FirstAttribute()); + member.pureType = strip(typeElement->GetText(), "Vk"); + member.type += member.pureType; + + child = typeElement->NextSibling(); + assert(child); + if (child->ToText()) + { + std::string value = trimEnd(child->Value()); + assert((value == "*") || (value == "**") || (value == "* const*")); + member.type += value; + child = child->NextSibling(); } - tinyxml2::XMLNode* child = readType(element->FirstChild(), member.type, member.pureType); vkData.dependencies.back().dependencies.insert(member.pureType); - assert((child->ToElement() && strcmp(child->Value(), "name") == 0)); - member.name = child->ToElement()->GetText(); + assert(child->ToElement()); + tinyxml2::XMLElement* nameElement = child->ToElement(); + assert((strcmp(nameElement->Value(), "name") == 0) && nameElement->GetText() && !nameElement->FirstAttribute()); + member.name = nameElement->GetText(); - member.arraySize = readArraySize(child, member.name); + member.arraySize = readArraySize(nameElement, member.name); } void readTag(tinyxml2::XMLElement * element, std::set & tags) @@ -2326,12 +2492,7 @@ void readTags(tinyxml2::XMLElement * element, std::set & tags) void readType(tinyxml2::XMLElement * element, VkData & vkData) { - std::map attributes; - for (auto attribute = element->FirstAttribute(); attribute; attribute = attribute->Next()) - { - assert(attributes.find(attribute->Name()) == attributes.end()); - attributes[attribute->Name()] = attribute->Value(); - } + std::map attributes = readAttributes(element); auto categoryIt = attributes.find("category"); if (categoryIt != attributes.end()) @@ -2356,7 +2517,7 @@ void readType(tinyxml2::XMLElement * element, VkData & vkData) } else if (categoryIt->second == "funcpointer") { - readTypeFuncpointer(element, vkData.dependencies); + readTypeFuncpointer(element, vkData.dependencies, attributes); } else if (categoryIt->second == "handle") { @@ -2370,11 +2531,11 @@ void readType(tinyxml2::XMLElement * element, VkData & vkData) #endif else if (categoryIt->second == "struct") { - readTypeStruct(element, vkData, false); + readTypeStruct(element, vkData, false, attributes); } else if (categoryIt->second == "union") { - readTypeStruct(element, vkData, true); + readTypeStruct(element, vkData, true, attributes); } else { @@ -2391,7 +2552,7 @@ void readType(tinyxml2::XMLElement * element, VkData & vkData) void readTypeName(tinyxml2::XMLElement * element, std::map const& attributes, std::list & dependencies) { - assert((attributes.size() == 1) || ((attributes.size() == 2) && (attributes.find("requires") != attributes.end()))); + checkAttributes(attributes, element->GetLineNum(), { {"name", {}} }, { {"requires", {}} }); assert(!element->FirstChildElement()); auto nameIt = attributes.find("name"); @@ -4390,8 +4551,8 @@ ${commands} if (!handleData.alias.empty()) { - os << std::endl - << " using " << handleData.alias << " = " << dependencyData.name << ";" << std::endl; + os << " using " << handleData.alias << " = " << dependencyData.name << ";" << std::endl + << std::endl; } // then the actual Deleter classes can be listed @@ -4919,6 +5080,13 @@ int main( int argc, char **argv ) } #if !defined(NDEBUG) +void skipFeatureRequire(tinyxml2::XMLElement * element) +{ + std::map attributes = readAttributes(element); + checkAttributes(attributes, element->GetLineNum(), { { "name",{} } }, {}); + assert(!element->FirstChildElement()); +} + void skipTypeEnum(tinyxml2::XMLElement * element, std::map const& attributes) { assert((attributes.find("category") != attributes.end()) && (attributes.find("category")->second == "enum"));