diff --git a/src/support/string.cpp b/src/support/string.cpp index f8f06a71804..28fa985d5a1 100644 --- a/src/support/string.cpp +++ b/src/support/string.cpp @@ -82,21 +82,70 @@ Split handleBracketingOperators(Split split) { return ret; } -bool wildcardMatch(const std::string& pattern, const std::string& value) { - for (size_t i = 0; i < pattern.size(); i++) { +bool wildcardMatch(const std::string& initialPattern, + const std::string& initialValue) { + // Avoid recursion, as strings can be very long. + struct Task { + std::string_view pattern; + std::string_view value; + size_t i; + // Whether we continue a task waiting on results of subtasks (see below). + bool continuation; + }; + std::vector tasks; + std::vector results; + // We start with the initial data. + tasks.push_back({initialPattern, initialValue, 0, false}); + while (!tasks.empty()) { + auto task = tasks.back(); + tasks.pop_back(); + auto& [pattern, value, i, continuation] = task; + if (continuation) { + // See below: We pushed two tasks, and must check if either matched. + auto num = results.size(); + assert(num >= 2); + auto result = results[num - 1] || results[num - 2]; + results.resize(num - 2); + results.push_back(result); + continue; + } + if (i == pattern.size()) { + // We reached the end. + results.push_back(value.size() == pattern.size()); + continue; + } if (pattern[i] == '*') { - return wildcardMatch(pattern.substr(i + 1), value.substr(i)) || - (value.size() > 0 && - wildcardMatch(pattern.substr(i), value.substr(i + 1))); + if (i >= value.size()) { + // A lone wildcard matches the empty string. + results.push_back(pattern.size() == (i + 1)); + continue; + } + // Push a continuation of us, and then two child tasks. We need one of the + // child tasks to be true for us to match. + tasks.push_back({pattern, value, i, true}); + // This child task matches if we can skip the '*' (we find what we want + // right after). + tasks.push_back({pattern.substr(i + 1), value.substr(i), 0, false}); + // This child task matches if we can skip a character in the value (the + // '*' matches something arbitary, and later we find what we want). + tasks.push_back({pattern.substr(i), value.substr(i + 1), 0, false}); + continue; } if (i >= value.size()) { - return false; + // We reached the end, and sizes do not match. + results.push_back(false); + continue; } if (pattern[i] != value[i]) { - return false; + // The data does not match. + results.push_back(false); + continue; } + // Proceed onwards. + tasks.push_back({pattern, value, i + 1, false}); } - return value.size() == pattern.size(); + assert(results.size() == 1); + return results[0]; } std::string trim(const std::string& input) { diff --git a/test/lit/passes/no-inline.wast b/test/lit/passes/no-inline.wast index 2e21105cb41..18a793a0728 100644 --- a/test/lit/passes/no-inline.wast +++ b/test/lit/passes/no-inline.wast @@ -638,6 +638,26 @@ ) ) + (func $very-long-name-we-should-not-error-on-even-though-it-is-very-very-long + ;; Test a long name. + ) + + ;; NO_FULL: (func $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long + ;; NO_FULL-NEXT: ) + ;; NO_BOTH: (func $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long + ;; NO_BOTH-NEXT: ) + (func $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long + ;; Test a long name with "maybe" in it. + ) + + ;; NO_FULL: (func $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long + ;; NO_FULL-NEXT: ) + ;; NO_BOTH: (func $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long + ;; NO_BOTH-NEXT: ) + (func $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long + ;; Test a long name with "maybe" in it, and a partial match earlier ("may"). + ) + ;; YES_ALL: (func $caller ;; YES_ALL-NEXT: (local $0 i32) ;; YES_ALL-NEXT: (local $1 i32) @@ -741,6 +761,18 @@ ;; YES_ALL-NEXT: ) ;; YES_ALL-NEXT: ) ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-even-though-it-is-very-very-long$4 + ;; YES_ALL-NEXT: (block + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long$5 + ;; YES_ALL-NEXT: (block + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: (block $__inlined_func$very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long$6 + ;; YES_ALL-NEXT: (block + ;; YES_ALL-NEXT: ) + ;; YES_ALL-NEXT: ) ;; YES_ALL-NEXT: ) ;; NO_PART: (func $caller ;; NO_PART-NEXT: (call $maybe-partial-or-full-1 @@ -755,6 +787,18 @@ ;; NO_PART-NEXT: (call $maybe-partial-or-full-2 ;; NO_PART-NEXT: (i32.const 1) ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-even-though-it-is-very-very-long + ;; NO_PART-NEXT: (block + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long$1 + ;; NO_PART-NEXT: (block + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: (block $__inlined_func$very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long$2 + ;; NO_PART-NEXT: (block + ;; NO_PART-NEXT: ) + ;; NO_PART-NEXT: ) ;; NO_PART-NEXT: ) ;; NO_FULL: (func $caller ;; NO_FULL-NEXT: (local $0 i32) @@ -817,6 +861,12 @@ ;; NO_FULL-NEXT: ) ;; NO_FULL-NEXT: ) ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-even-though-it-is-very-very-long$4 + ;; NO_FULL-NEXT: (block + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: ) + ;; NO_FULL-NEXT: (call $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long) + ;; NO_FULL-NEXT: (call $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long) ;; NO_FULL-NEXT: ) ;; NO_BOTH: (func $caller ;; NO_BOTH-NEXT: (call $maybe-partial-or-full-1 @@ -831,6 +881,12 @@ ;; NO_BOTH-NEXT: (call $maybe-partial-or-full-2 ;; NO_BOTH-NEXT: (i32.const 1) ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (block $__inlined_func$very-long-name-we-should-not-error-on-even-though-it-is-very-very-long + ;; NO_BOTH-NEXT: (block + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: ) + ;; NO_BOTH-NEXT: (call $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long) + ;; NO_BOTH-NEXT: (call $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long) ;; NO_BOTH-NEXT: ) (func $caller ;; In YES_ALL we will fully inline all of these. In NO_FULL we will partially @@ -851,6 +907,9 @@ (call $maybe-partial-or-full-2 (i32.const 1) ) + (call $very-long-name-we-should-not-error-on-even-though-it-is-very-very-long) + (call $very-long-name-we-should-not-error-on-maybe-even-though-it-is-very-very-long) + (call $very-long-name-we-may-should-not-error-on-maybe-even-though-it-is-very-very-long) ) ) ;; NO_FULL: (func $byn-split-outlined-B$maybe-partial-or-full-1 (param $x i32)