<div dir="ltr"><div>Hi Jim,</div><div><br></div><div>Thanks for the encouragement! It's unfortunate that you missed the notifications and couldn't contribute to the code reviews, but I'm glad you think the code looks fine.</div><div><br></div><div>Of course I don't mind adding these as a follow-up, it would be a shame if I didn't expose the new feature for CLI users after doing all the work to get to this point. In fact, I was planning to ask about it after landing the change, so thank you for the details about how to proceed next :)</div><div dir="ltr"><br></div><div dir="ltr">On Wed, Oct 19, 2022 at 2:00 PM Jim Ingham <<a href="mailto:jingham@apple.com">jingham@apple.com</a>> wrote:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Jorge,<br>
<br>
It's great to see you worked your way through this change!<br>
<br>
Something in our mail pipeline is dropping all my lldb-commits and review comment notifications. Still haven't figured out who is doing that, so I didn't get a chance to look over the final version.<br>
<br>
The code looks fine, but nobody will know how to use this if they don't read lldb source code fairly carefully, or browse our tests.<br>
<br>
To finish off the feature, there should be an example to put in the examples directory, and there should be a paragraph showing how to use it in lldb/doc/use/formatting.rst.<br>
<br>
It would also be good to add something an option to "type summary add" and "type synthetic add" to indicate that the name passed is neither a type name nor a regex but a recognizer function instead. Maybe -R/--recognizer-function?<br>
<br>
Is it too horrible of me to ask you to do these as a follow-up? Otherwise I fear you will be the only one to use this feature...<br>
<br>
Jim<br>
<br>
<br>
> On Oct 19, 2022, at 12:54 PM, Jorge Gorbe Moya via lldb-commits <<a href="mailto:lldb-commits@lists.llvm.org" target="_blank">lldb-commits@lists.llvm.org</a>> wrote:<br>
> <br>
> <br>
> Author: Jorge Gorbe Moya<br>
> Date: 2022-10-19T12:53:38-07:00<br>
> New Revision: d76566417e592cfac9c710f82575473b1b4a9285<br>
> <br>
> URL: <a href="https://github.com/llvm/llvm-project/commit/d76566417e592cfac9c710f82575473b1b4a9285" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/d76566417e592cfac9c710f82575473b1b4a9285</a><br>
> DIFF: <a href="https://github.com/llvm/llvm-project/commit/d76566417e592cfac9c710f82575473b1b4a9285.diff" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/d76566417e592cfac9c710f82575473b1b4a9285.diff</a><br>
> <br>
> LOG: [lldb] Add matching based on Python callbacks for data formatters.<br>
> <br>
> This patch adds a new matching method for data formatters, in addition<br>
> to the existing exact typename and regex-based matching. The new method<br>
> allows users to specify the name of a Python callback function that<br>
> takes a `SBType` object and decides whether the type is a match or not.<br>
> <br>
> Here is an overview of the changes performed:<br>
> <br>
> - Add a new `eFormatterMatchCallback` matching type, and logic to handle<br>
> it in `TypeMatcher` and `SBTypeNameSpecifier`.<br>
> <br>
> - Extend `FormattersMatchCandidate` instances with a pointer to the<br>
> current `ScriptInterpreter` and the `TypeImpl` corresponding to the<br>
> candidate type, so we can run registered callbacks and pass the type<br>
> to them. All matcher search functions now receive a<br>
> `FormattersMatchCandidate` instead of a type name.<br>
> <br>
> - Add some glue code to ScriptInterpreterPython and the SWIG bindings to<br>
> allow calling a formatter matching callback. Most of this code is<br>
> modeled after the equivalent code for watchpoint callback functions.<br>
> <br>
> - Add an API test for the new callback-based matching feature.<br>
> <br>
> For more context, please check the RFC thread where this feature was<br>
> originally discussed:<br>
> <a href="https://discourse.llvm.org/t/rfc-python-callback-for-data-formatters-type-matching/64204/11" rel="noreferrer" target="_blank">https://discourse.llvm.org/t/rfc-python-callback-for-data-formatters-type-matching/64204/11</a><br>
> <br>
> Differential Revision: <a href="https://reviews.llvm.org/D135648" rel="noreferrer" target="_blank">https://reviews.llvm.org/D135648</a><br>
> <br>
> Added: <br>
> lldb/test/API/functionalities/data-formatter/callback-matching/Makefile<br>
> lldb/test/API/functionalities/data-formatter/callback-matching/TestDataFormatterCallbackMatching.py<br>
> lldb/test/API/functionalities/data-formatter/callback-matching/formatters_with_callback.py<br>
> lldb/test/API/functionalities/data-formatter/callback-matching/main.cpp<br>
> <br>
> Modified: <br>
> lldb/bindings/python/python-swigsafecast.swig<br>
> lldb/bindings/python/python-wrapper.swig<br>
> lldb/include/lldb/API/SBType.h<br>
> lldb/include/lldb/DataFormatters/DataVisualization.h<br>
> lldb/include/lldb/DataFormatters/FormatClasses.h<br>
> lldb/include/lldb/DataFormatters/FormatManager.h<br>
> lldb/include/lldb/DataFormatters/FormattersContainer.h<br>
> lldb/include/lldb/DataFormatters/TypeCategory.h<br>
> lldb/include/lldb/DataFormatters/TypeCategoryMap.h<br>
> lldb/include/lldb/Interpreter/ScriptInterpreter.h<br>
> lldb/include/lldb/Target/Language.h<br>
> lldb/include/lldb/lldb-enumerations.h<br>
> lldb/source/API/SBTypeNameSpecifier.cpp<br>
> lldb/source/Commands/CommandObjectType.cpp<br>
> lldb/source/DataFormatters/DataVisualization.cpp<br>
> lldb/source/DataFormatters/FormatManager.cpp<br>
> lldb/source/DataFormatters/TypeCategory.cpp<br>
> lldb/source/DataFormatters/TypeCategoryMap.cpp<br>
> lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp<br>
> lldb/source/Plugins/Language/ObjC/ObjCLanguage.h<br>
> lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h<br>
> lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp<br>
> lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h<br>
> lldb/source/Target/Language.cpp<br>
> lldb/unittests/DataFormatter/FormattersContainerTest.cpp<br>
> lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp<br>
> <br>
> Removed: <br>
> <br>
> <br>
> <br>
> ################################################################################<br>
> diff --git a/lldb/bindings/python/python-swigsafecast.swig b/lldb/bindings/python/python-swigsafecast.swig<br>
> index eb684133abef..aa5e8e50a2d9 100644<br>
> --- a/lldb/bindings/python/python-swigsafecast.swig<br>
> +++ b/lldb/bindings/python/python-swigsafecast.swig<br>
> @@ -97,6 +97,10 @@ PythonObject ToSWIGWrapper(lldb::ExecutionContextRefSP ctx_sp) {<br>
> SWIGTYPE_p_lldb__SBExecutionContext);<br>
> }<br>
> <br>
> +PythonObject ToSWIGWrapper(lldb::TypeImplSP type_impl_sp) {<br>
> + return ToSWIGHelper(new lldb::SBType(type_impl_sp), SWIGTYPE_p_lldb__SBType);<br>
> +}<br>
> +<br>
> PythonObject ToSWIGWrapper(const TypeSummaryOptions &summary_options) {<br>
> return ToSWIGHelper(new lldb::SBTypeSummaryOptions(summary_options),<br>
> SWIGTYPE_p_lldb__SBTypeSummaryOptions);<br>
> <br>
> diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig<br>
> index 626fc47bebb9..adac8a405ab9 100644<br>
> --- a/lldb/bindings/python/python-wrapper.swig<br>
> +++ b/lldb/bindings/python/python-wrapper.swig<br>
> @@ -90,6 +90,32 @@ bool lldb_private::LLDBSwigPythonWatchpointCallbackFunction(<br>
> return stop_at_watchpoint;<br>
> }<br>
> <br>
> +// This function is called by<br>
> +// ScriptInterpreterPython::FormatterMatchingCallbackFunction and it's used when<br>
> +// a data formatter provides the name of a callback to inspect a candidate type<br>
> +// before considering a match.<br>
> +bool lldb_private::LLDBSwigPythonFormatterCallbackFunction(<br>
> + const char *python_function_name, const char *session_dictionary_name,<br>
> + lldb::TypeImplSP type_impl_sp) {<br>
> +<br>
> + PyErr_Cleaner py_err_cleaner(true);<br>
> +<br>
> + auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(<br>
> + session_dictionary_name);<br>
> + auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(<br>
> + python_function_name, dict);<br>
> +<br>
> + if (!pfunc.IsAllocated())<br>
> + return false;<br>
> +<br>
> + PythonObject result =<br>
> + pfunc(ToSWIGWrapper(type_impl_sp), dict);<br>
> +<br>
> + // Only if everything goes okay and the function returns True we'll consider<br>
> + // it a match.<br>
> + return result.get() == Py_True;<br>
> +}<br>
> +<br>
> bool lldb_private::LLDBSwigPythonCallTypeScript(<br>
> const char *python_function_name, const void *session_dictionary,<br>
> const lldb::ValueObjectSP &valobj_sp, void **pyfunct_wrapper,<br>
> <br>
> diff --git a/lldb/include/lldb/API/SBType.h b/lldb/include/lldb/API/SBType.h<br>
> index aa45aeeec476..215e03fad99b 100644<br>
> --- a/lldb/include/lldb/API/SBType.h<br>
> +++ b/lldb/include/lldb/API/SBType.h<br>
> @@ -106,6 +106,7 @@ class SBType {<br>
> SBType();<br>
> <br>
> SBType(const lldb::SBType &rhs);<br>
> + SBType(const lldb::TypeImplSP &);<br>
> <br>
> ~SBType();<br>
> <br>
> @@ -239,7 +240,6 @@ class SBType {<br>
> <br>
> SBType(const lldb_private::CompilerType &);<br>
> SBType(const lldb::TypeSP &);<br>
> - SBType(const lldb::TypeImplSP &);<br>
> };<br>
> <br>
> class SBTypeList {<br>
> <br>
> diff --git a/lldb/include/lldb/DataFormatters/DataVisualization.h b/lldb/include/lldb/DataFormatters/DataVisualization.h<br>
> index 7be07d65acdd..5aea29132b8f 100644<br>
> --- a/lldb/include/lldb/DataFormatters/DataVisualization.h<br>
> +++ b/lldb/include/lldb/DataFormatters/DataVisualization.h<br>
> @@ -51,7 +51,7 @@ class DataVisualization {<br>
> GetSyntheticChildren(ValueObject &valobj, lldb::DynamicValueType use_dynamic);<br>
> <br>
> static bool<br>
> - AnyMatches(ConstString type_name,<br>
> + AnyMatches(const FormattersMatchCandidate &candidate_type,<br>
> TypeCategoryImpl::FormatCategoryItems items =<br>
> TypeCategoryImpl::ALL_ITEM_TYPES,<br>
> bool only_enabled = true, const char **matching_category = nullptr,<br>
> <br>
> diff --git a/lldb/include/lldb/DataFormatters/FormatClasses.h b/lldb/include/lldb/DataFormatters/FormatClasses.h<br>
> index ac2b070a55cd..a6bc3a354253 100644<br>
> --- a/lldb/include/lldb/DataFormatters/FormatClasses.h<br>
> +++ b/lldb/include/lldb/DataFormatters/FormatClasses.h<br>
> @@ -17,6 +17,7 @@<br>
> #include "lldb/DataFormatters/TypeFormat.h"<br>
> #include "lldb/DataFormatters/TypeSummary.h"<br>
> #include "lldb/DataFormatters/TypeSynthetic.h"<br>
> +#include "lldb/Interpreter/ScriptInterpreter.h"<br>
> #include "lldb/Symbol/CompilerType.h"<br>
> #include "lldb/Symbol/Type.h"<br>
> #include "lldb/lldb-enumerations.h"<br>
> @@ -73,13 +74,22 @@ class FormattersMatchCandidate {<br>
> }<br>
> };<br>
> <br>
> - FormattersMatchCandidate(ConstString name, Flags flags)<br>
> - : m_type_name(name), m_flags(flags) {}<br>
> + FormattersMatchCandidate(ConstString name,<br>
> + ScriptInterpreter *script_interpreter, TypeImpl type,<br>
> + Flags flags)<br>
> + : m_type_name(name), m_script_interpreter(script_interpreter),<br>
> + m_type(type), m_flags(flags) {}<br>
> <br>
> ~FormattersMatchCandidate() = default;<br>
> <br>
> ConstString GetTypeName() const { return m_type_name; }<br>
> <br>
> + TypeImpl GetType() const { return m_type; }<br>
> +<br>
> + ScriptInterpreter *GetScriptInterpreter() const {<br>
> + return m_script_interpreter;<br>
> + }<br>
> +<br>
> bool DidStripPointer() const { return m_flags.stripped_pointer; }<br>
> <br>
> bool DidStripReference() const { return m_flags.stripped_reference; }<br>
> @@ -101,6 +111,10 @@ class FormattersMatchCandidate {<br>
> <br>
> private:<br>
> ConstString m_type_name;<br>
> + // If a formatter provides a matching callback function, we need the script<br>
> + // interpreter and the type object (as an argument to the callback).<br>
> + ScriptInterpreter *m_script_interpreter;<br>
> + TypeImpl m_type;<br>
> Flags m_flags;<br>
> };<br>
> <br>
> <br>
> diff --git a/lldb/include/lldb/DataFormatters/FormatManager.h b/lldb/include/lldb/DataFormatters/FormatManager.h<br>
> index 594addd1f083..295d3b84342a 100644<br>
> --- a/lldb/include/lldb/DataFormatters/FormatManager.h<br>
> +++ b/lldb/include/lldb/DataFormatters/FormatManager.h<br>
> @@ -128,12 +128,12 @@ class FormatManager : public IFormatChangeListener {<br>
> GetSyntheticChildren(ValueObject &valobj, lldb::DynamicValueType use_dynamic);<br>
> <br>
> bool<br>
> - AnyMatches(ConstString type_name,<br>
> + AnyMatches(const FormattersMatchCandidate &candidate_type,<br>
> TypeCategoryImpl::FormatCategoryItems items =<br>
> TypeCategoryImpl::ALL_ITEM_TYPES,<br>
> bool only_enabled = true, const char **matching_category = nullptr,<br>
> TypeCategoryImpl::FormatCategoryItems *matching_type = nullptr) {<br>
> - return m_categories_map.AnyMatches(type_name, items, only_enabled,<br>
> + return m_categories_map.AnyMatches(candidate_type, items, only_enabled,<br>
> matching_category, matching_type);<br>
> }<br>
> <br>
> <br>
> diff --git a/lldb/include/lldb/DataFormatters/FormattersContainer.h b/lldb/include/lldb/DataFormatters/FormattersContainer.h<br>
> index 8a93c0345cbe..fd046e773b69 100644<br>
> --- a/lldb/include/lldb/DataFormatters/FormattersContainer.h<br>
> +++ b/lldb/include/lldb/DataFormatters/FormattersContainer.h<br>
> @@ -39,12 +39,16 @@ class IFormatChangeListener {<br>
> <br>
> /// Class for matching type names.<br>
> class TypeMatcher {<br>
> + /// Type name for exact match, or name of the python callback if m_match_type<br>
> + /// is `eFormatterMatchCallback`.<br>
> + ConstString m_name;<br>
> RegularExpression m_type_name_regex;<br>
> - ConstString m_type_name;<br>
> /// Indicates what kind of matching strategy should be used:<br>
> - /// - eFormatterMatchExact: match the exact type name in m_type_name.<br>
> + /// - eFormatterMatchExact: match the exact type name in m_name.<br>
> /// - eFormatterMatchRegex: match using the RegularExpression object<br>
> /// `m_type_name_regex` instead.<br>
> + /// - eFormatterMatchCallback: run the function in m_name to decide if a type<br>
> + /// matches or not.<br>
> lldb::FormatterMatchType m_match_type;<br>
> <br>
> // if the user tries to add formatters for, say, "struct Foo" those will not<br>
> @@ -73,7 +77,7 @@ class TypeMatcher {<br>
> TypeMatcher() = delete;<br>
> /// Creates a matcher that accepts any type with exactly the given type name.<br>
> TypeMatcher(ConstString type_name)<br>
> - : m_type_name(type_name), m_match_type(lldb::eFormatterMatchExact) {}<br>
> + : m_name(type_name), m_match_type(lldb::eFormatterMatchExact) {}<br>
> /// Creates a matcher that accepts any type matching the given regex.<br>
> TypeMatcher(RegularExpression regex)<br>
> : m_type_name_regex(std::move(regex)),<br>
> @@ -81,27 +85,44 @@ class TypeMatcher {<br>
> /// Creates a matcher using the matching type and string from the given type<br>
> /// name specifier.<br>
> TypeMatcher(lldb::TypeNameSpecifierImplSP type_specifier)<br>
> - : m_type_name(type_specifier->GetName()),<br>
> + : m_name(type_specifier->GetName()),<br>
> m_match_type(type_specifier->GetMatchType()) {<br>
> if (m_match_type == lldb::eFormatterMatchRegex)<br>
> m_type_name_regex = RegularExpression(type_specifier->GetName());<br>
> }<br>
> <br>
> - /// True iff this matches the given type name.<br>
> - bool Matches(ConstString type_name) const {<br>
> - if (m_match_type == lldb::eFormatterMatchRegex)<br>
> + /// True iff this matches the given type.<br>
> + bool Matches(FormattersMatchCandidate candidate_type) const {<br>
> + ConstString type_name = candidate_type.GetTypeName();<br>
> + switch (m_match_type) {<br>
> + case lldb::eFormatterMatchExact:<br>
> + return m_name == type_name ||<br>
> + StripTypeName(m_name) == StripTypeName(type_name);<br>
> + case lldb::eFormatterMatchRegex:<br>
> return m_type_name_regex.Execute(type_name.GetStringRef());<br>
> - return m_type_name == type_name ||<br>
> - StripTypeName(m_type_name) == StripTypeName(type_name);<br>
> + case lldb::eFormatterMatchCallback:<br>
> + // CommandObjectType{Synth,Filter}Add tries to prevent the user from<br>
> + // creating both a synthetic child provider and a filter for the same type<br>
> + // in the same category, but we don't have a type object at that point, so<br>
> + // it creates a dummy candidate without type or script interpreter.<br>
> + // Skip callback matching in these cases.<br>
> + if (candidate_type.GetScriptInterpreter())<br>
> + return candidate_type.GetScriptInterpreter()->FormatterCallbackFunction(<br>
> + m_name.AsCString(),<br>
> + std::make_shared<TypeImpl>(candidate_type.GetType()));<br>
> + }<br>
> + return false;<br>
> }<br>
> <br>
> lldb::FormatterMatchType GetMatchType() const { return m_match_type; }<br>
> <br>
> /// Returns the underlying match string for this TypeMatcher.<br>
> ConstString GetMatchString() const {<br>
> + if (m_match_type == lldb::eFormatterMatchExact)<br>
> + return StripTypeName(m_name);<br>
> if (m_match_type == lldb::eFormatterMatchRegex)<br>
> - return ConstString(m_type_name_regex.GetText());<br>
> - return StripTypeName(m_type_name);<br>
> + return ConstString(m_type_name_regex.GetText());<br>
> + return m_name;<br>
> }<br>
> <br>
> /// Returns true if this TypeMatcher and the given one were most created by<br>
> @@ -155,10 +176,11 @@ template <typename ValueType> class FormattersContainer {<br>
> return false;<br>
> }<br>
> <br>
> - bool Get(ConstString type, ValueSP &entry) {<br>
> + // Finds the first formatter in the container that matches `candidate`.<br>
> + bool Get(FormattersMatchCandidate candidate, ValueSP &entry) {<br>
> std::lock_guard<std::recursive_mutex> guard(m_map_mutex);<br>
> for (auto &formatter : llvm::reverse(m_map)) {<br>
> - if (formatter.first.Matches(type)) {<br>
> + if (formatter.first.Matches(candidate)) {<br>
> entry = formatter.second;<br>
> return true;<br>
> }<br>
> @@ -166,9 +188,11 @@ template <typename ValueType> class FormattersContainer {<br>
> return false;<br>
> }<br>
> <br>
> + // Finds the first match between candidate types in `candidates` and<br>
> + // formatters in this container.<br>
> bool Get(const FormattersMatchVector &candidates, ValueSP &entry) {<br>
> for (const FormattersMatchCandidate &candidate : candidates) {<br>
> - if (Get(candidate.GetTypeName(), entry)) {<br>
> + if (Get(candidate, entry)) {<br>
> if (candidate.IsMatch(entry) == false) {<br>
> entry.reset();<br>
> continue;<br>
> <br>
> diff --git a/lldb/include/lldb/DataFormatters/TypeCategory.h b/lldb/include/lldb/DataFormatters/TypeCategory.h<br>
> index bad39aa676af..884a27d76e05 100644<br>
> --- a/lldb/include/lldb/DataFormatters/TypeCategory.h<br>
> +++ b/lldb/include/lldb/DataFormatters/TypeCategory.h<br>
> @@ -105,10 +105,10 @@ template <typename FormatterImpl> class TieredFormatterContainer {<br>
> return false;<br>
> }<br>
> <br>
> - bool AnyMatches(ConstString type_name) {<br>
> + bool AnyMatches(const FormattersMatchCandidate &candidate) {<br>
> std::shared_ptr<FormatterImpl> entry;<br>
> for (auto sc : m_subcontainers) {<br>
> - if (sc->Get(type_name, entry))<br>
> + if (sc->Get(FormattersMatchVector{candidate}, entry))<br>
> return true;<br>
> }<br>
> return false;<br>
> @@ -346,7 +346,7 @@ class TypeCategoryImpl {<br>
> <br>
> std::string GetDescription();<br>
> <br>
> - bool AnyMatches(ConstString type_name,<br>
> + bool AnyMatches(const FormattersMatchCandidate &candidate_type,<br>
> FormatCategoryItems items = ALL_ITEM_TYPES,<br>
> bool only_enabled = true,<br>
> const char **matching_category = nullptr,<br>
> <br>
> diff --git a/lldb/include/lldb/DataFormatters/TypeCategoryMap.h b/lldb/include/lldb/DataFormatters/TypeCategoryMap.h<br>
> index 45dbb306aa75..d4f7634c90b0 100644<br>
> --- a/lldb/include/lldb/DataFormatters/TypeCategoryMap.h<br>
> +++ b/lldb/include/lldb/DataFormatters/TypeCategoryMap.h<br>
> @@ -17,6 +17,7 @@<br>
> #include "lldb/lldb-enumerations.h"<br>
> #include "lldb/lldb-public.h"<br>
> <br>
> +#include "lldb/DataFormatters/FormatClasses.h"<br>
> #include "lldb/DataFormatters/FormattersContainer.h"<br>
> #include "lldb/DataFormatters/TypeCategory.h"<br>
> <br>
> @@ -69,7 +70,7 @@ class TypeCategoryMap {<br>
> lldb::TypeCategoryImplSP GetAtIndex(uint32_t);<br>
> <br>
> bool<br>
> - AnyMatches(ConstString type_name,<br>
> + AnyMatches(const FormattersMatchCandidate &candidate_type,<br>
> TypeCategoryImpl::FormatCategoryItems items =<br>
> TypeCategoryImpl::ALL_ITEM_TYPES,<br>
> bool only_enabled = true, const char **matching_category = nullptr,<br>
> <br>
> diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h<br>
> index cb3cafaf2ed5..f34ce43e946e 100644<br>
> --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h<br>
> +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h<br>
> @@ -418,6 +418,14 @@ class ScriptInterpreter : public PluginInterface {<br>
> return false;<br>
> }<br>
> <br>
> + // Calls the specified formatter matching Python function and returns its<br>
> + // result (true if it's a match, false if we should keep looking for a<br>
> + // matching formatter).<br>
> + virtual bool FormatterCallbackFunction(const char *function_name,<br>
> + lldb::TypeImplSP type_impl_sp) {<br>
> + return true;<br>
> + }<br>
> +<br>
> virtual void Clear() {<br>
> // Clean up any ref counts to SBObjects that might be in global variables<br>
> }<br>
> <br>
> diff --git a/lldb/include/lldb/Target/Language.h b/lldb/include/lldb/Target/Language.h<br>
> index fa79aaee0574..89136cc5e0ff 100644<br>
> --- a/lldb/include/lldb/Target/Language.h<br>
> +++ b/lldb/include/lldb/Target/Language.h<br>
> @@ -175,7 +175,7 @@ class Language : public PluginInterface {<br>
> virtual HardcodedFormatters::HardcodedSyntheticFinder<br>
> GetHardcodedSynthetics();<br>
> <br>
> - virtual std::vector<ConstString><br>
> + virtual std::vector<FormattersMatchCandidate><br>
> GetPossibleFormattersMatches(ValueObject &valobj,<br>
> lldb::DynamicValueType use_dynamic);<br>
> <br>
> <br>
> diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h<br>
> index 2ac1a74214b4..3ba29a301382 100644<br>
> --- a/lldb/include/lldb/lldb-enumerations.h<br>
> +++ b/lldb/include/lldb/lldb-enumerations.h<br>
> @@ -835,8 +835,9 @@ enum TemplateArgumentKind {<br>
> enum FormatterMatchType {<br>
> eFormatterMatchExact,<br>
> eFormatterMatchRegex,<br>
> + eFormatterMatchCallback,<br>
> <br>
> - eLastFormatterMatchType = eFormatterMatchRegex,<br>
> + eLastFormatterMatchType = eFormatterMatchCallback,<br>
> };<br>
> <br>
> /// Options that can be set for a formatter to alter its behavior. Not<br>
> <br>
> diff --git a/lldb/source/API/SBTypeNameSpecifier.cpp b/lldb/source/API/SBTypeNameSpecifier.cpp<br>
> index d1dc2953c9b9..8a6eb086a9b1 100644<br>
> --- a/lldb/source/API/SBTypeNameSpecifier.cpp<br>
> +++ b/lldb/source/API/SBTypeNameSpecifier.cpp<br>
> @@ -99,10 +99,14 @@ bool SBTypeNameSpecifier::GetDescription(<br>
> lldb::SBStream &description, lldb::DescriptionLevel description_level) {<br>
> LLDB_INSTRUMENT_VA(this, description, description_level);<br>
> <br>
> + lldb::FormatterMatchType match_type = GetMatchType();<br>
> + const char *match_type_str =<br>
> + (match_type == eFormatterMatchExact ? "plain"<br>
> + : match_type == eFormatterMatchRegex ? "regex"<br>
> + : "callback");<br>
> if (!IsValid())<br>
> return false;<br>
> - description.Printf("SBTypeNameSpecifier(%s,%s)", GetName(),<br>
> - IsRegex() ? "regex" : "plain");<br>
> + description.Printf("SBTypeNameSpecifier(%s,%s)", GetName(), match_type_str);<br>
> return true;<br>
> }<br>
> <br>
> <br>
> diff --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp<br>
> index 63d3c6979ec3..ccbe7922e65f 100644<br>
> --- a/lldb/source/Commands/CommandObjectType.cpp<br>
> +++ b/lldb/source/Commands/CommandObjectType.cpp<br>
> @@ -11,6 +11,7 @@<br>
> #include "lldb/Core/Debugger.h"<br>
> #include "lldb/Core/IOHandler.h"<br>
> #include "lldb/DataFormatters/DataVisualization.h"<br>
> +#include "lldb/DataFormatters/FormatClasses.h"<br>
> #include "lldb/Host/Config.h"<br>
> #include "lldb/Host/OptionParser.h"<br>
> #include "lldb/Interpreter/CommandInterpreter.h"<br>
> @@ -2302,7 +2303,13 @@ bool CommandObjectTypeSynthAdd::AddSynth(ConstString type_name,<br>
> // an actual type name. Matching a regex string against registered regexes<br>
> // doesn't work.<br>
> if (type == eRegularSynth) {<br>
> - if (category->AnyMatches(type_name, eFormatCategoryItemFilter, false)) {<br>
> + // It's not generally possible to get a type object here. For example, this<br>
> + // command can be run before loading any binaries. Do just a best-effort<br>
> + // name-based lookup here to try to prevent conflicts.<br>
> + FormattersMatchCandidate candidate_type(type_name, nullptr, TypeImpl(),<br>
> + FormattersMatchCandidate::Flags());<br>
> + if (category->AnyMatches(candidate_type, eFormatCategoryItemFilter,<br>
> + false)) {<br>
> if (error)<br>
> error->SetErrorStringWithFormat("cannot add synthetic for type %s when "<br>
> "filter is defined in same category!",<br>
> @@ -2427,7 +2434,14 @@ class CommandObjectTypeFilterAdd : public CommandObjectParsed {<br>
> // if `type_name` is an actual type name. Matching a regex string against<br>
> // registered regexes doesn't work.<br>
> if (type == eRegularFilter) {<br>
> - if (category->AnyMatches(type_name, eFormatCategoryItemSynth, false)) {<br>
> + // It's not generally possible to get a type object here. For example,<br>
> + // this command can be run before loading any binaries. Do just a<br>
> + // best-effort name-based lookup here to try to prevent conflicts.<br>
> + FormattersMatchCandidate candidate_type(<br>
> + type_name, nullptr, TypeImpl(), FormattersMatchCandidate::Flags());<br>
> + lldb::SyntheticChildrenSP entry;<br>
> + if (category->AnyMatches(candidate_type, eFormatCategoryItemSynth,<br>
> + false)) {<br>
> if (error)<br>
> error->SetErrorStringWithFormat("cannot add filter for type %s when "<br>
> "synthetic is defined in same "<br>
> <br>
> diff --git a/lldb/source/DataFormatters/DataVisualization.cpp b/lldb/source/DataFormatters/DataVisualization.cpp<br>
> index 53832492aa25..036c9372baf8 100644<br>
> --- a/lldb/source/DataFormatters/DataVisualization.cpp<br>
> +++ b/lldb/source/DataFormatters/DataVisualization.cpp<br>
> @@ -66,10 +66,11 @@ DataVisualization::GetSyntheticForType(lldb::TypeNameSpecifierImplSP type_sp) {<br>
> }<br>
> <br>
> bool DataVisualization::AnyMatches(<br>
> - ConstString type_name, TypeCategoryImpl::FormatCategoryItems items,<br>
> - bool only_enabled, const char **matching_category,<br>
> + const FormattersMatchCandidate &candidate_type,<br>
> + TypeCategoryImpl::FormatCategoryItems items, bool only_enabled,<br>
> + const char **matching_category,<br>
> TypeCategoryImpl::FormatCategoryItems *matching_type) {<br>
> - return GetFormatManager().AnyMatches(type_name, items, only_enabled,<br>
> + return GetFormatManager().AnyMatches(candidate_type, items, only_enabled,<br>
> matching_category, matching_type);<br>
> }<br>
> <br>
> <br>
> diff --git a/lldb/source/DataFormatters/FormatManager.cpp b/lldb/source/DataFormatters/FormatManager.cpp<br>
> index db9f6057e842..166264df9933 100644<br>
> --- a/lldb/source/DataFormatters/FormatManager.cpp<br>
> +++ b/lldb/source/DataFormatters/FormatManager.cpp<br>
> @@ -11,6 +11,7 @@<br>
> #include "lldb/Core/Debugger.h"<br>
> #include "lldb/DataFormatters/FormattersHelpers.h"<br>
> #include "lldb/DataFormatters/LanguageCategory.h"<br>
> +#include "lldb/Interpreter/ScriptInterpreter.h"<br>
> #include "lldb/Target/ExecutionContext.h"<br>
> #include "lldb/Target/Language.h"<br>
> #include "lldb/Utility/LLDBLog.h"<br>
> @@ -178,19 +179,24 @@ void FormatManager::GetPossibleMatches(<br>
> FormattersMatchCandidate::Flags current_flags, bool root_level) {<br>
> compiler_type = compiler_type.GetTypeForFormatters();<br>
> ConstString type_name(compiler_type.GetTypeName());<br>
> + ScriptInterpreter *script_interpreter =<br>
> + valobj.GetTargetSP()->GetDebugger().GetScriptInterpreter();<br>
> if (valobj.GetBitfieldBitSize() > 0) {<br>
> StreamString sstring;<br>
> sstring.Printf("%s:%d", type_name.AsCString(), valobj.GetBitfieldBitSize());<br>
> ConstString bitfieldname(sstring.GetString());<br>
> - entries.push_back({bitfieldname, current_flags});<br>
> + entries.push_back({bitfieldname, script_interpreter,<br>
> + TypeImpl(compiler_type), current_flags});<br>
> }<br>
> <br>
> if (!compiler_type.IsMeaninglessWithoutDynamicResolution()) {<br>
> - entries.push_back({type_name, current_flags});<br>
> + entries.push_back({type_name, script_interpreter, TypeImpl(compiler_type),<br>
> + current_flags});<br>
> <br>
> ConstString display_type_name(compiler_type.GetTypeName());<br>
> if (display_type_name != type_name)<br>
> - entries.push_back({display_type_name, current_flags});<br>
> + entries.push_back({display_type_name, script_interpreter,<br>
> + TypeImpl(compiler_type), current_flags});<br>
> }<br>
> <br>
> for (bool is_rvalue_ref = true, j = true;<br>
> @@ -245,9 +251,9 @@ void FormatManager::GetPossibleMatches(<br>
> for (lldb::LanguageType language_type :<br>
> GetCandidateLanguages(valobj.GetObjectRuntimeLanguage())) {<br>
> if (Language *language = Language::FindPlugin(language_type)) {<br>
> - for (ConstString candidate :<br>
> + for (const FormattersMatchCandidate& candidate :<br>
> language->GetPossibleFormattersMatches(valobj, use_dynamic)) {<br>
> - entries.push_back({candidate, current_flags});<br>
> + entries.push_back(candidate);<br>
> }<br>
> }<br>
> }<br>
> <br>
> diff --git a/lldb/source/DataFormatters/TypeCategory.cpp b/lldb/source/DataFormatters/TypeCategory.cpp<br>
> index 1d5674968245..4d8663ea9c03 100644<br>
> --- a/lldb/source/DataFormatters/TypeCategory.cpp<br>
> +++ b/lldb/source/DataFormatters/TypeCategory.cpp<br>
> @@ -184,20 +184,15 @@ uint32_t TypeCategoryImpl::GetCount(FormatCategoryItems items) {<br>
> return count;<br>
> }<br>
> <br>
> -bool TypeCategoryImpl::AnyMatches(ConstString type_name,<br>
> - FormatCategoryItems items, bool only_enabled,<br>
> - const char **matching_category,<br>
> - FormatCategoryItems *matching_type) {<br>
> +bool TypeCategoryImpl::AnyMatches(<br>
> + const FormattersMatchCandidate &candidate_type, FormatCategoryItems items,<br>
> + bool only_enabled, const char **matching_category,<br>
> + FormatCategoryItems *matching_type) {<br>
> if (!IsEnabled() && only_enabled)<br>
> return false;<br>
> <br>
> - lldb::TypeFormatImplSP format_sp;<br>
> - lldb::TypeSummaryImplSP summary_sp;<br>
> - TypeFilterImpl::SharedPointer filter_sp;<br>
> - ScriptedSyntheticChildren::SharedPointer synth_sp;<br>
> -<br>
> if (items & eFormatCategoryItemFormat) {<br>
> - if (m_format_cont.AnyMatches(type_name)) {<br>
> + if (m_format_cont.AnyMatches(candidate_type)) {<br>
> if (matching_category)<br>
> *matching_category = m_name.GetCString();<br>
> if (matching_type)<br>
> @@ -207,7 +202,7 @@ bool TypeCategoryImpl::AnyMatches(ConstString type_name,<br>
> }<br>
> <br>
> if (items & eFormatCategoryItemSummary) {<br>
> - if (m_summary_cont.AnyMatches(type_name)) {<br>
> + if (m_summary_cont.AnyMatches(candidate_type)) {<br>
> if (matching_category)<br>
> *matching_category = m_name.GetCString();<br>
> if (matching_type)<br>
> @@ -217,7 +212,7 @@ bool TypeCategoryImpl::AnyMatches(ConstString type_name,<br>
> }<br>
> <br>
> if (items & eFormatCategoryItemFilter) {<br>
> - if (m_filter_cont.AnyMatches(type_name)) {<br>
> + if (m_filter_cont.AnyMatches(candidate_type)) {<br>
> if (matching_category)<br>
> *matching_category = m_name.GetCString();<br>
> if (matching_type)<br>
> @@ -227,7 +222,7 @@ bool TypeCategoryImpl::AnyMatches(ConstString type_name,<br>
> }<br>
> <br>
> if (items & eFormatCategoryItemSynth) {<br>
> - if (m_synth_cont.AnyMatches(type_name)) {<br>
> + if (m_synth_cont.AnyMatches(candidate_type)) {<br>
> if (matching_category)<br>
> *matching_category = m_name.GetCString();<br>
> if (matching_type)<br>
> <br>
> diff --git a/lldb/source/DataFormatters/TypeCategoryMap.cpp b/lldb/source/DataFormatters/TypeCategoryMap.cpp<br>
> index aa8387b4deec..55635173cc8c 100644<br>
> --- a/lldb/source/DataFormatters/TypeCategoryMap.cpp<br>
> +++ b/lldb/source/DataFormatters/TypeCategoryMap.cpp<br>
> @@ -154,14 +154,15 @@ bool TypeCategoryMap::Get(uint32_t pos, ValueSP &entry) {<br>
> }<br>
> <br>
> bool TypeCategoryMap::AnyMatches(<br>
> - ConstString type_name, TypeCategoryImpl::FormatCategoryItems items,<br>
> - bool only_enabled, const char **matching_category,<br>
> + const FormattersMatchCandidate &candidate_type,<br>
> + TypeCategoryImpl::FormatCategoryItems items, bool only_enabled,<br>
> + const char **matching_category,<br>
> TypeCategoryImpl::FormatCategoryItems *matching_type) {<br>
> std::lock_guard<std::recursive_mutex> guard(m_map_mutex);<br>
> <br>
> MapIterator pos, end = m_map.end();<br>
> for (pos = m_map.begin(); pos != end; pos++) {<br>
> - if (pos->second->AnyMatches(type_name, items, only_enabled,<br>
> + if (pos->second->AnyMatches(candidate_type, items, only_enabled,<br>
> matching_category, matching_type))<br>
> return true;<br>
> }<br>
> <br>
> diff --git a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp<br>
> index 11d5b0813b58..9cbb40419167 100644<br>
> --- a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp<br>
> +++ b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.cpp<br>
> @@ -12,6 +12,7 @@<br>
> <br>
> #include "Plugins/ExpressionParser/Clang/ClangUtil.h"<br>
> #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"<br>
> +#include "lldb/Core/Debugger.h"<br>
> #include "lldb/Core/PluginManager.h"<br>
> #include "lldb/Core/ValueObject.h"<br>
> #include "lldb/DataFormatters/DataVisualization.h"<br>
> @@ -931,10 +932,10 @@ lldb::TypeCategoryImplSP ObjCLanguage::GetFormatters() {<br>
> return g_category;<br>
> }<br>
> <br>
> -std::vector<ConstString><br>
> +std::vector<FormattersMatchCandidate><br>
> ObjCLanguage::GetPossibleFormattersMatches(ValueObject &valobj,<br>
> lldb::DynamicValueType use_dynamic) {<br>
> - std::vector<ConstString> result;<br>
> + std::vector<FormattersMatchCandidate> result;<br>
> <br>
> if (use_dynamic == lldb::eNoDynamicValues)<br>
> return result;<br>
> @@ -959,7 +960,10 @@ ObjCLanguage::GetPossibleFormattersMatches(ValueObject &valobj,<br>
> if (!objc_class_sp)<br>
> break;<br>
> if (ConstString name = objc_class_sp->GetClassName())<br>
> - result.push_back(name);<br>
> + result.push_back(<br>
> + {name, valobj.GetTargetSP()->GetDebugger().GetScriptInterpreter(),<br>
> + TypeImpl(objc_class_sp->GetType()),<br>
> + FormattersMatchCandidate::Flags{}});<br>
> } while (false);<br>
> }<br>
> <br>
> <br>
> diff --git a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h<br>
> index 914452086db7..b61348a3280e 100644<br>
> --- a/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h<br>
> +++ b/lldb/source/Plugins/Language/ObjC/ObjCLanguage.h<br>
> @@ -108,7 +108,7 @@ class ObjCLanguage : public Language {<br>
> <br>
> lldb::TypeCategoryImplSP GetFormatters() override;<br>
> <br>
> - std::vector<ConstString><br>
> + std::vector<FormattersMatchCandidate><br>
> GetPossibleFormattersMatches(ValueObject &valobj,<br>
> lldb::DynamicValueType use_dynamic) override;<br>
> <br>
> <br>
> diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h<br>
> index 4df235356737..7e18b0ef0804 100644<br>
> --- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h<br>
> +++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h<br>
> @@ -75,6 +75,10 @@ bool LLDBSwigPythonWatchpointCallbackFunction(<br>
> const char *python_function_name, const char *session_dictionary_name,<br>
> const lldb::StackFrameSP &sb_frame, const lldb::WatchpointSP &sb_wp);<br>
> <br>
> +bool LLDBSwigPythonFormatterCallbackFunction(<br>
> + const char *python_function_name, const char *session_dictionary_name,<br>
> + lldb::TypeImplSP type_impl_sp);<br>
> +<br>
> bool LLDBSwigPythonCallTypeScript(const char *python_function_name,<br>
> const void *session_dictionary,<br>
> const lldb::ValueObjectSP &valobj_sp,<br>
> <br>
> diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp<br>
> index d0f67a5684c5..37e3c94df870 100644<br>
> --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp<br>
> +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp<br>
> @@ -2154,6 +2154,14 @@ bool ScriptInterpreterPythonImpl::GetScriptedSummary(<br>
> return ret_val;<br>
> }<br>
> <br>
> +bool ScriptInterpreterPythonImpl::FormatterCallbackFunction(<br>
> + const char *python_function_name, TypeImplSP type_impl_sp) {<br>
> + Locker py_lock(this,<br>
> + Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);<br>
> + return LLDBSwigPythonFormatterCallbackFunction(<br>
> + python_function_name, m_dictionary_name.c_str(), type_impl_sp);<br>
> +}<br>
> +<br>
> bool ScriptInterpreterPythonImpl::BreakpointCallbackFunction(<br>
> void *baton, StoppointCallbackContext *context, user_id_t break_id,<br>
> user_id_t break_loc_id) {<br>
> <br>
> diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h<br>
> index 3b80c67d201a..f4875bfb8d18 100644<br>
> --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h<br>
> +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h<br>
> @@ -202,6 +202,9 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython {<br>
> const TypeSummaryOptions &options,<br>
> std::string &retval) override;<br>
> <br>
> + bool FormatterCallbackFunction(const char *function_name,<br>
> + lldb::TypeImplSP type_impl_sp) override;<br>
> +<br>
> bool GetDocumentationForItem(const char *item, std::string &dest) override;<br>
> <br>
> bool GetShortHelpForCommandObject(StructuredData::GenericSP cmd_obj_sp,<br>
> <br>
> diff --git a/lldb/source/Target/Language.cpp b/lldb/source/Target/Language.cpp<br>
> index 6df36aeeb7b7..92431005cba9 100644<br>
> --- a/lldb/source/Target/Language.cpp<br>
> +++ b/lldb/source/Target/Language.cpp<br>
> @@ -144,7 +144,7 @@ Language::GetHardcodedSynthetics() {<br>
> return {};<br>
> }<br>
> <br>
> -std::vector<ConstString><br>
> +std::vector<FormattersMatchCandidate><br>
> Language::GetPossibleFormattersMatches(ValueObject &valobj,<br>
> lldb::DynamicValueType use_dynamic) {<br>
> return {};<br>
> <br>
> diff --git a/lldb/test/API/functionalities/data-formatter/callback-matching/Makefile b/lldb/test/API/functionalities/data-formatter/callback-matching/Makefile<br>
> new file mode 100644<br>
> index 000000000000..99998b20bcb0<br>
> --- /dev/null<br>
> +++ b/lldb/test/API/functionalities/data-formatter/callback-matching/Makefile<br>
> @@ -0,0 +1,3 @@<br>
> +CXX_SOURCES := main.cpp<br>
> +<br>
> +include Makefile.rules<br>
> <br>
> diff --git a/lldb/test/API/functionalities/data-formatter/callback-matching/TestDataFormatterCallbackMatching.py b/lldb/test/API/functionalities/data-formatter/callback-matching/TestDataFormatterCallbackMatching.py<br>
> new file mode 100644<br>
> index 000000000000..91403d6d5f16<br>
> --- /dev/null<br>
> +++ b/lldb/test/API/functionalities/data-formatter/callback-matching/TestDataFormatterCallbackMatching.py<br>
> @@ -0,0 +1,49 @@<br>
> +"""<br>
> +Test lldb data formatter callback-based matching.<br>
> +"""<br>
> +<br>
> +import lldb<br>
> +from lldbsuite.test.decorators import *<br>
> +from lldbsuite.test.lldbtest import *<br>
> +from lldbsuite.test import lldbutil<br>
> +<br>
> +<br>
> +class PythonSynthDataFormatterTestCase(TestBase):<br>
> +<br>
> + def setUp(self):<br>
> + # Call super's setUp().<br>
> + TestBase.setUp(self)<br>
> + # Find the line number to break at.<br>
> + self.line = line_number('main.cpp', '// Set break point at this line.')<br>
> +<br>
> + def test_callback_matchers(self):<br>
> + """Test data formatter commands."""<br>
> + self.build()<br>
> +<br>
> + _, process, thread, _ = lldbutil.run_to_line_breakpoint(<br>
> + self, lldb.SBFileSpec("main.cpp"), self.line)<br>
> +<br>
> + # Print derived without a formatter.<br>
> + self.expect("frame variable derived",<br>
> + substrs=['x = 2222',<br>
> + 'y = 3333'])<br>
> +<br>
> + # now set up a summary function that uses a python callback to match<br>
> + # classes that derive from `Base`.<br>
> + self.runCmd("command script import --allow-reload ./formatters_with_callback.py")<br>
> +<br>
> + # Now `derived` should use our callback summary + synthetic children.<br>
> + self.expect("frame variable derived",<br>
> + substrs=['hello from callback summary',<br>
> + 'synthetic_child = 9999'])<br>
> +<br>
> + # But not other classes.<br>
> + self.expect("frame variable base", matching=False,<br>
> + substrs=['hello from callback summary'])<br>
> + self.expect("frame variable base",<br>
> + substrs=['x = 1111'])<br>
> +<br>
> + self.expect("frame variable nd", matching=False,<br>
> + substrs=['hello from callback summary'])<br>
> + self.expect("frame variable nd",<br>
> + substrs=['z = 4444'])<br>
> <br>
> diff --git a/lldb/test/API/functionalities/data-formatter/callback-matching/formatters_with_callback.py b/lldb/test/API/functionalities/data-formatter/callback-matching/formatters_with_callback.py<br>
> new file mode 100644<br>
> index 000000000000..60e919a94352<br>
> --- /dev/null<br>
> +++ b/lldb/test/API/functionalities/data-formatter/callback-matching/formatters_with_callback.py<br>
> @@ -0,0 +1,39 @@<br>
> +import lldb<br>
> +<br>
> +def derives_from_base(sbtype, internal_dict):<br>
> + for base in sbtype.get_bases_array():<br>
> + if base.GetName() == "Base":<br>
> + return True<br>
> + return False<br>
> +<br>
> +<br>
> +class SynthProvider:<br>
> + def __init__(self, valobj, dict):<br>
> + self.valobj = valobj<br>
> +<br>
> + def num_children(self):<br>
> + return 1<br>
> +<br>
> + def get_child_index(self, name):<br>
> + return 0<br>
> +<br>
> + def get_child_at_index(self, index):<br>
> + if index == 0:<br>
> + return self.valobj.CreateValueFromExpression("synthetic_child",<br>
> + "9999")<br>
> + return None<br>
> +<br>
> +<br>
> +def __lldb_init_module(debugger, dict):<br>
> + cat = debugger.CreateCategory("callback_formatters")<br>
> + cat.AddTypeSummary(<br>
> + lldb.SBTypeNameSpecifier("formatters_with_callback.derives_from_base",<br>
> + lldb.eFormatterMatchCallback),<br>
> + lldb.SBTypeSummary.CreateWithScriptCode(<br>
> + "return 'hello from callback summary'"))<br>
> + cat.AddTypeSynthetic(<br>
> + lldb.SBTypeNameSpecifier('formatters_with_callback.derives_from_base',<br>
> + lldb.eFormatterMatchCallback),<br>
> + lldb.SBTypeSynthetic.CreateWithClassName(<br>
> + 'formatters_with_callback.SynthProvider'))<br>
> + cat.SetEnabled(True)<br>
> <br>
> diff --git a/lldb/test/API/functionalities/data-formatter/callback-matching/main.cpp b/lldb/test/API/functionalities/data-formatter/callback-matching/main.cpp<br>
> new file mode 100644<br>
> index 000000000000..7732d87342a9<br>
> --- /dev/null<br>
> +++ b/lldb/test/API/functionalities/data-formatter/callback-matching/main.cpp<br>
> @@ -0,0 +1,16 @@<br>
> +struct Base { int x; };<br>
> +struct Derived : public Base { int y; };<br>
> +<br>
> +struct NonDerived { int z; };<br>
> +<br>
> +int main()<br>
> +{<br>
> + Base base = {1111};<br>
> +<br>
> + Derived derived;<br>
> + derived.x = 2222;<br>
> + derived.y = 3333;<br>
> +<br>
> + NonDerived nd = {4444};<br>
> + return 0; // Set break point at this line.<br>
> +}<br>
> <br>
> diff --git a/lldb/unittests/DataFormatter/FormattersContainerTest.cpp b/lldb/unittests/DataFormatter/FormattersContainerTest.cpp<br>
> index a28212391eae..41b01adfb9ec 100644<br>
> --- a/lldb/unittests/DataFormatter/FormattersContainerTest.cpp<br>
> +++ b/lldb/unittests/DataFormatter/FormattersContainerTest.cpp<br>
> @@ -7,12 +7,20 @@<br>
> //===----------------------------------------------------------------------===//<br>
> <br>
> #include "lldb/DataFormatters/FormattersContainer.h"<br>
> +#include "lldb/DataFormatters/FormatClasses.h"<br>
> <br>
> #include "gtest/gtest.h"<br>
> <br>
> using namespace lldb;<br>
> using namespace lldb_private;<br>
> <br>
> +// Creates a dummy candidate with just a type name in order to test the string<br>
> +// matching (exact name match and regex match) paths.<br>
> +FormattersMatchCandidate CandidateFromTypeName(const char *type_name) {<br>
> + return FormattersMatchCandidate(ConstString(type_name), nullptr, TypeImpl(),<br>
> + FormattersMatchCandidate::Flags());<br>
> +}<br>
> +<br>
> // All the prefixes that the exact name matching will strip from the type.<br>
> static const std::vector<std::string> exact_name_prefixes = {<br>
> "", // no prefix.<br>
> @@ -25,63 +33,63 @@ TEST(TypeMatcherTests, ExactName) {<br>
> SCOPED_TRACE("Prefix: " + prefix);<br>
> <br>
> TypeMatcher matcher(ConstString(prefix + "Name"));<br>
> - EXPECT_TRUE(matcher.Matches(ConstString("class Name")));<br>
> - EXPECT_TRUE(matcher.Matches(ConstString("struct Name")));<br>
> - EXPECT_TRUE(matcher.Matches(ConstString("union Name")));<br>
> - EXPECT_TRUE(matcher.Matches(ConstString("enum Name")));<br>
> - EXPECT_TRUE(matcher.Matches(ConstString("Name")));<br>
> -<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("Name ")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("ame")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("Nam")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("am")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("a")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString(" ")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("class N")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("class ")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("class")));<br>
> + EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("class Name")));<br>
> + EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("struct Name")));<br>
> + EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("union Name")));<br>
> + EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("enum Name")));<br>
> + EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("Name")));<br>
> +<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("Name ")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("ame")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("Nam")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("am")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("a")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName(" ")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("class N")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("class ")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("class")));<br>
> }<br>
> }<br>
> <br>
> // TypeMatcher that uses a regex to match a type name.<br>
> TEST(TypeMatcherTests, RegexName) {<br>
> TypeMatcher matcher(RegularExpression("^a[a-z]c$"));<br>
> - EXPECT_TRUE(matcher.Matches(ConstString("abc")));<br>
> - EXPECT_TRUE(matcher.Matches(ConstString("azc")));<br>
> + EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("abc")));<br>
> + EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("azc")));<br>
> <br>
> // FIXME: This isn't consistent with the 'exact' type name matches above.<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("class abc")));<br>
> -<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("abbc")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString(" abc")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("abc ")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString(" abc ")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("XabcX")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("ac")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("a[a-z]c")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("aAc")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("ABC")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("class abc")));<br>
> +<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("abbc")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName(" abc")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("abc ")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName(" abc ")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("XabcX")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("ac")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("a[a-z]c")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("aAc")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("ABC")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("")));<br>
> }<br>
> <br>
> // TypeMatcher that only searches the type name.<br>
> TEST(TypeMatcherTests, RegexMatchPart) {<br>
> TypeMatcher matcher(RegularExpression("a[a-z]c"));<br>
> - EXPECT_TRUE(matcher.Matches(ConstString("class abc")));<br>
> - EXPECT_TRUE(matcher.Matches(ConstString("abc")));<br>
> - EXPECT_TRUE(matcher.Matches(ConstString(" abc ")));<br>
> - EXPECT_TRUE(matcher.Matches(ConstString("azc")));<br>
> - EXPECT_TRUE(matcher.Matches(ConstString("abc ")));<br>
> - EXPECT_TRUE(matcher.Matches(ConstString(" abc ")));<br>
> - EXPECT_TRUE(matcher.Matches(ConstString(" abc")));<br>
> - EXPECT_TRUE(matcher.Matches(ConstString("XabcX")));<br>
> -<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("abbc")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("ac")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("a[a-z]c")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("aAc")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("ABC")));<br>
> - EXPECT_FALSE(matcher.Matches(ConstString("")));<br>
> + EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("class abc")));<br>
> + EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("abc")));<br>
> + EXPECT_TRUE(matcher.Matches(CandidateFromTypeName(" abc ")));<br>
> + EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("azc")));<br>
> + EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("abc ")));<br>
> + EXPECT_TRUE(matcher.Matches(CandidateFromTypeName(" abc ")));<br>
> + EXPECT_TRUE(matcher.Matches(CandidateFromTypeName(" abc")));<br>
> + EXPECT_TRUE(matcher.Matches(CandidateFromTypeName("XabcX")));<br>
> +<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("abbc")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("ac")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("a[a-z]c")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("aAc")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("ABC")));<br>
> + EXPECT_FALSE(matcher.Matches(CandidateFromTypeName("")));<br>
> }<br>
> <br>
> // GetMatchString for exact type name matchers.<br>
> <br>
> diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp<br>
> index 6ac4606b5a84..87e4a03ee77b 100644<br>
> --- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp<br>
> +++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp<br>
> @@ -67,6 +67,12 @@ bool lldb_private::LLDBSwigPythonWatchpointCallbackFunction(<br>
> return false;<br>
> }<br>
> <br>
> +bool lldb_private::LLDBSwigPythonFormatterCallbackFunction(<br>
> + const char *python_function_name, const char *session_dictionary_name,<br>
> + lldb::TypeImplSP type_impl_sp) {<br>
> + return false;<br>
> +}<br>
> +<br>
> bool lldb_private::LLDBSwigPythonCallTypeScript(<br>
> const char *python_function_name, const void *session_dictionary,<br>
> const lldb::ValueObjectSP &valobj_sp, void **pyfunct_wrapper,<br>
> <br>
> <br>
> <br>
> _______________________________________________<br>
> lldb-commits mailing list<br>
> <a href="mailto:lldb-commits@lists.llvm.org" target="_blank">lldb-commits@lists.llvm.org</a><br>
> <a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits</a><br>
<br>
</blockquote></div></div>