handle super cycles while searching for a key or for a tag
A super cycle may be found during the validation, leading to an infinite loop. For example, calling find_key on an unknown key defined in the .cfg file may trigger this behavior if a cycle exists. To handle this, a new version of these functions was introduced, keeping the original in the class' private scope. During the search there is a bookkeeping helper that will cause the routine to return nullptr if the search goes back to the initial tag context.
This commit is contained in:
parent
f7511ea731
commit
c64416bbb6
2 changed files with 42 additions and 7 deletions
|
@ -108,10 +108,24 @@ void wml_tag::add_link(const std::string& link)
|
|||
|
||||
const wml_key* wml_tag::find_key(const std::string& name, const config& match, bool ignore_super) const
|
||||
{
|
||||
auto visited = std::vector<const wml_tag*>();
|
||||
return find_key(name, match, ignore_super, visited);
|
||||
}
|
||||
|
||||
const wml_key* wml_tag::find_key(const std::string& name, const config& match, bool ignore_super, std::vector<const wml_tag*>& visited) const
|
||||
{
|
||||
// Returns nullptr if a super cycle is detected.
|
||||
if (std::find(visited.begin(), visited.end(), this) != visited.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
visited.push_back(this);
|
||||
|
||||
// Check the conditions first, so that conditional definitions
|
||||
// override base definitions in the event of duplicates.
|
||||
for(auto& cond : conditions_) {
|
||||
if(cond.matches(match)) {
|
||||
// Not considered for super cycle detection as super tags are ignored.
|
||||
if(auto key = cond.find_key(name, match, true)) {
|
||||
return key;
|
||||
}
|
||||
|
@ -137,13 +151,13 @@ const wml_key* wml_tag::find_key(const std::string& name, const config& match, b
|
|||
for(auto& cond : conditions_) {
|
||||
if(cond.matches(match)) {
|
||||
// This results in a little redundancy (checking things twice) but at least it still works.
|
||||
if(auto key = cond.find_key(name, match, false)) {
|
||||
if(auto key = cond.find_key(name, match, false, visited)) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(auto& super_tag : super_refs_) {
|
||||
if(const wml_key* found_key = super_tag->find_key(name, match)) {
|
||||
if(const wml_key* found_key = super_tag->find_key(name, match, false, visited)) {
|
||||
return found_key;
|
||||
}
|
||||
}
|
||||
|
@ -164,6 +178,19 @@ const std::string* wml_tag::find_link(const std::string& name) const
|
|||
|
||||
const wml_tag* wml_tag::find_tag(const std::string& fullpath, const wml_tag& root, const config& match, bool ignore_super) const
|
||||
{
|
||||
auto visited = std::vector<const wml_tag*>();
|
||||
return find_tag(fullpath, root, match, ignore_super, visited);
|
||||
}
|
||||
|
||||
const wml_tag* wml_tag::find_tag(const std::string& fullpath, const wml_tag& root, const config& match, bool ignore_super, std::vector<const wml_tag*>& visited) const
|
||||
{
|
||||
// Returns nullptr if a super cycle is detected.
|
||||
if (std::find(visited.begin(), visited.end(), this) != visited.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
visited.push_back(this);
|
||||
|
||||
if(fullpath.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -183,6 +210,7 @@ const wml_tag* wml_tag::find_tag(const std::string& fullpath, const wml_tag& roo
|
|||
// override base definitions in the event of duplicates.
|
||||
for(auto& cond : conditions_) {
|
||||
if(cond.matches(match)) {
|
||||
// Not considered for super cycle detection as super tags are ignored.
|
||||
if(auto tag = cond.find_tag(fullpath, root, match, true)) {
|
||||
return tag;
|
||||
}
|
||||
|
@ -194,13 +222,14 @@ const wml_tag* wml_tag::find_tag(const std::string& fullpath, const wml_tag& roo
|
|||
if(next_path.empty()) {
|
||||
return &(it_tags->second);
|
||||
} else {
|
||||
return it_tags->second.find_tag(next_path, root, match);
|
||||
return it_tags->second.find_tag(next_path, root, match, false, visited);
|
||||
}
|
||||
}
|
||||
|
||||
const auto it_links = links_.find(name);
|
||||
if(it_links != links_.end()) {
|
||||
return root.find_tag(it_links->second + "/" + next_path, root, match);
|
||||
// Reset cycle detection on links as we restart from the root.
|
||||
return root.find_tag(it_links->second + "/" + next_path, root, match, false);
|
||||
}
|
||||
|
||||
const auto it_fuzzy = std::find_if(tags_.begin(), tags_.end(), [&name](const tag_map::value_type& tag){
|
||||
|
@ -213,7 +242,7 @@ const wml_tag* wml_tag::find_tag(const std::string& fullpath, const wml_tag& roo
|
|||
if(next_path.empty()) {
|
||||
return &(it_fuzzy->second);
|
||||
} else {
|
||||
return it_tags->second.find_tag(next_path, root, match);
|
||||
return it_tags->second.find_tag(next_path, root, match, false, visited);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,13 +250,13 @@ const wml_tag* wml_tag::find_tag(const std::string& fullpath, const wml_tag& roo
|
|||
for(auto& cond : conditions_) {
|
||||
if(cond.matches(match)) {
|
||||
// This results in a little redundancy (checking things twice) but at least it still works.
|
||||
if(auto tag = cond.find_tag(fullpath, root, match, false)) {
|
||||
if(auto tag = cond.find_tag(fullpath, root, match, false, visited)) {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(auto& super_tag : super_refs_) {
|
||||
if(const wml_tag* found_tag = super_tag->find_tag(fullpath, root, match)) {
|
||||
if(const wml_tag* found_tag = super_tag->find_tag(fullpath, root, match, false, visited)) {
|
||||
return found_tag;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -404,6 +404,12 @@ private:
|
|||
|
||||
/** Expands all "super", storing direct references for easier access. */
|
||||
void expand(wml_tag& root);
|
||||
|
||||
/** Finds a key with super bookkeeping to handle super cycles. */
|
||||
const wml_key* find_key(const std::string& name, const config& match, bool ignore_super, std::vector<const wml_tag*>& visited) const;
|
||||
|
||||
/** Finds a tag with super bookkeeping to handle super cycles. */
|
||||
const wml_tag* find_tag(const std::string& fullpath, const wml_tag& root, const config& match, bool ignore_super, std::vector<const wml_tag*>& visited) const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Reference in a new issue