[Lldb-commits] [lldb] d765664 - [lldb] Add matching based on Python callbacks for data formatters.

Jorge Gorbe Moya via lldb-commits lldb-commits at lists.llvm.org
Fri Oct 28 17:17:50 PDT 2022


Sent you https://reviews.llvm.org/D137000 for review :)

On Wed, Oct 19, 2022 at 2:16 PM Jorge Gorbe Moya <jgorbe at google.com> wrote:

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


More information about the lldb-commits mailing list