diff options
Diffstat (limited to 'source/Plugins/Language/ObjC/Cocoa.cpp')
| -rw-r--r-- | source/Plugins/Language/ObjC/Cocoa.cpp | 967 | 
1 files changed, 967 insertions, 0 deletions
diff --git a/source/Plugins/Language/ObjC/Cocoa.cpp b/source/Plugins/Language/ObjC/Cocoa.cpp new file mode 100644 index 000000000000..aa6e476b6131 --- /dev/null +++ b/source/Plugins/Language/ObjC/Cocoa.cpp @@ -0,0 +1,967 @@ +//===-- Cocoa.cpp -----------------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "Cocoa.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "lldb/DataFormatters/StringPrinter.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/Language.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/ProcessStructReader.h" + +#include "NSString.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +bool +lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) +{ +    ProcessSP process_sp = valobj.GetProcessSP(); +    if (!process_sp) +        return false; +     +    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); +     +    if (!runtime) +        return false; +     +    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); +     +    if (!descriptor.get() || !descriptor->IsValid()) +        return false; +     +    uint32_t ptr_size = process_sp->GetAddressByteSize(); +     +    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); +     +    if (!valobj_addr) +        return false; +     +    const char* class_name = descriptor->GetClassName().GetCString(); +     +    if (!class_name || !*class_name) +        return false; +     +    if (!strcmp(class_name,"NSBundle")) +    { +        uint64_t offset = 5 * ptr_size; +        ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), true)); +         +        StreamString summary_stream; +        bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream, options); +        if (was_nsstring_ok && summary_stream.GetSize() > 0) +        { +            stream.Printf("%s",summary_stream.GetData()); +            return true; +        } +    } +    // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle] +    // which is encoded differently and needs to be handled by running code +    return ExtractSummaryFromObjCExpression(valobj, "NSString*", "bundlePath", stream, options.GetLanguage()); +} + +bool +lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) +{ +    ProcessSP process_sp = valobj.GetProcessSP(); +    if (!process_sp) +        return false; +     +    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); +     +    if (!runtime) +        return false; +     +    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); +     +    if (!descriptor.get() || !descriptor->IsValid()) +        return false; +     +    uint32_t ptr_size = process_sp->GetAddressByteSize(); +     +    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); +     +    if (!valobj_addr) +        return false; +     +    const char* class_name = descriptor->GetClassName().GetCString(); +     +    if (!class_name || !*class_name) +        return false; +     +    if (!strcmp(class_name,"__NSTimeZone")) +    { +        uint64_t offset = ptr_size; +        ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetCompilerType(), true)); +        StreamString summary_stream; +        bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream, options); +        if (was_nsstring_ok && summary_stream.GetSize() > 0) +        { +            stream.Printf("%s",summary_stream.GetData()); +            return true; +        } +    } +    return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream, options.GetLanguage()); +} + +bool +lldb_private::formatters::NSNotificationSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) +{ +    ProcessSP process_sp = valobj.GetProcessSP(); +    if (!process_sp) +        return false; +     +    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); +     +    if (!runtime) +        return false; +     +    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); +     +    if (!descriptor.get() || !descriptor->IsValid()) +        return false; +     +    uint32_t ptr_size = process_sp->GetAddressByteSize(); +     +    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); +     +    if (!valobj_addr) +        return false; +     +    const char* class_name = descriptor->GetClassName().GetCString(); +     +    if (!class_name || !*class_name) +        return false; +     +    if (!strcmp(class_name,"NSConcreteNotification")) +    { +        uint64_t offset = ptr_size; +        ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetCompilerType(), true)); +        StreamString summary_stream; +        bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream, options); +        if (was_nsstring_ok && summary_stream.GetSize() > 0) +        { +            stream.Printf("%s",summary_stream.GetData()); +            return true; +        } +    } +    // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle] +    // which is encoded differently and needs to be handled by running code +    return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream, options.GetLanguage()); +} + +bool +lldb_private::formatters::NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) +{ +    ProcessSP process_sp = valobj.GetProcessSP(); +    if (!process_sp) +        return false; +     +    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); +     +    if (!runtime) +        return false; +     +    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); +     +    if (!descriptor.get() || !descriptor->IsValid()) +        return false; +     +    uint32_t ptr_size = process_sp->GetAddressByteSize(); +     +    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); +     +    if (!valobj_addr) +        return false; +     +    const char* class_name = descriptor->GetClassName().GetCString(); +     +    if (!class_name || !*class_name) +        return false; +     +    uint64_t port_number = 0; +     +    do +    { +        if (!strcmp(class_name,"NSMachPort")) +        { +            uint64_t offset = (ptr_size == 4 ? 12 : 20); +            Error error; +            port_number = process_sp->ReadUnsignedIntegerFromMemory(offset+valobj_addr, 4, 0, error); +            if (error.Success()) +                break; +        } +        if (!ExtractValueFromObjCExpression(valobj, "int", "machPort", port_number)) +            return false; +    } while (false); +     +    stream.Printf("mach port: %u",(uint32_t)(port_number & 0x00000000FFFFFFFF)); +    return true; +} + +bool +lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) +{ +    ProcessSP process_sp = valobj.GetProcessSP(); +    if (!process_sp) +        return false; +     +    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); +     +    if (!runtime) +        return false; +     +    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); +     +    if (!descriptor.get() || !descriptor->IsValid()) +        return false; +     +    uint32_t ptr_size = process_sp->GetAddressByteSize(); +     +    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); +     +    if (!valobj_addr) +        return false; +     +    const char* class_name = descriptor->GetClassName().GetCString(); +     +    if (!class_name || !*class_name) +        return false; +     +    uint64_t count = 0; +     +    do { +        if (!strcmp(class_name,"NSIndexSet") || !strcmp(class_name,"NSMutableIndexSet")) +        { +            Error error; +            uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 4, 0, error); +            if (error.Fail()) +                return false; +            // this means the set is empty - count = 0 +            if ((mode & 1) == 1) +            { +                count = 0; +                break; +            } +            if ((mode & 2) == 2) +                mode = 1; // this means the set only has one range +            else +                mode = 2; // this means the set has multiple ranges +            if (mode == 1) +            { +                count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+3*ptr_size, ptr_size, 0, error); +                if (error.Fail()) +                    return false; +            } +            else +            { +                // read a pointer to the data at 2*ptr_size +                count = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, ptr_size, 0, error); +                if (error.Fail()) +                    return false; +                // read the data at 2*ptr_size from the first location +                count = process_sp->ReadUnsignedIntegerFromMemory(count+2*ptr_size, ptr_size, 0, error); +                if (error.Fail()) +                    return false; +            } +        } +        else +        { +            if (!ExtractValueFromObjCExpression(valobj, "unsigned long long int", "count", count)) +                return false; +        } +    }  while (false); +    stream.Printf("%" PRIu64 " index%s", +                  count, +                  (count == 1 ? "" : "es")); +    return true; +} + +static void +NSNumber_FormatChar (ValueObject& valobj, +                     Stream& stream, +                     char value, +                     lldb::LanguageType lang) +{ +    static ConstString g_TypeHint("NSNumber:char"); +     +    std::string prefix,suffix; +    if (Language* language = Language::FindPlugin(lang)) +    { +        if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix)) +        { +            prefix.clear(); +            suffix.clear(); +        } +    } +     +    stream.Printf("%s%hhd%s", +                  prefix.c_str(), +                  value, +                  suffix.c_str()); +} + +static void +NSNumber_FormatShort (ValueObject& valobj, +                      Stream& stream, +                      short value, +                      lldb::LanguageType lang) +{ +    static ConstString g_TypeHint("NSNumber:short"); +     +    std::string prefix,suffix; +    if (Language* language = Language::FindPlugin(lang)) +    { +        if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix)) +        { +            prefix.clear(); +            suffix.clear(); +        } +    } +     +    stream.Printf("%s%hd%s", +                  prefix.c_str(), +                  value, +                  suffix.c_str()); +} + +static void +NSNumber_FormatInt (ValueObject& valobj, +                    Stream& stream, +                    int value, +                    lldb::LanguageType lang) +{ +    static ConstString g_TypeHint("NSNumber:int"); +     +    std::string prefix,suffix; +    if (Language* language = Language::FindPlugin(lang)) +    { +        if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix)) +        { +            prefix.clear(); +            suffix.clear(); +        } +    } +     +    stream.Printf("%s%d%s", +                  prefix.c_str(), +                  value, +                  suffix.c_str()); +} + +static void +NSNumber_FormatLong (ValueObject& valobj, +                     Stream& stream, +                     uint64_t value, +                     lldb::LanguageType lang) +{ +    static ConstString g_TypeHint("NSNumber:long"); +     +    std::string prefix,suffix; +    if (Language* language = Language::FindPlugin(lang)) +    { +        if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix)) +        { +            prefix.clear(); +            suffix.clear(); +        } +    } +     +    stream.Printf("%s%" PRId64 "%s", +                  prefix.c_str(), +                  value, +                  suffix.c_str()); +} + +static void +NSNumber_FormatFloat (ValueObject& valobj, +                      Stream& stream, +                      float value, +                      lldb::LanguageType lang) +{ +    static ConstString g_TypeHint("NSNumber:float"); +     +    std::string prefix,suffix; +    if (Language* language = Language::FindPlugin(lang)) +    { +        if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix)) +        { +            prefix.clear(); +            suffix.clear(); +        } +    } +     +    stream.Printf("%s%f%s", +                  prefix.c_str(), +                  value, +                  suffix.c_str()); +} + +static void +NSNumber_FormatDouble (ValueObject& valobj, +                       Stream& stream, +                       double value, +                       lldb::LanguageType lang) +{ +    static ConstString g_TypeHint("NSNumber:double"); +     +    std::string prefix,suffix; +    if (Language* language = Language::FindPlugin(lang)) +    { +        if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, suffix)) +        { +            prefix.clear(); +            suffix.clear(); +        } +    } +     +    stream.Printf("%s%g%s", +                  prefix.c_str(), +                  value, +                  suffix.c_str()); +} + +bool +lldb_private::formatters::NSNumberSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) +{ +    ProcessSP process_sp = valobj.GetProcessSP(); +    if (!process_sp) +        return false; +     +    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); +     +    if (!runtime) +        return false; +     +    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); +     +    if (!descriptor.get() || !descriptor->IsValid()) +        return false; +     +    uint32_t ptr_size = process_sp->GetAddressByteSize(); +     +    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); +     +    if (!valobj_addr) +        return false; +     +    const char* class_name = descriptor->GetClassName().GetCString(); +     +    if (!class_name || !*class_name) +        return false; +     +    if (!strcmp(class_name,"NSNumber") || !strcmp(class_name,"__NSCFNumber")) +    { +        uint64_t value = 0; +        uint64_t i_bits = 0; +        if (descriptor->GetTaggedPointerInfo(&i_bits,&value)) +        { +            switch (i_bits) +            { +                case 0: +                    NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); +                    break; +                case 1: +                case 4: +                    NSNumber_FormatShort(valobj, stream, (short)value, options.GetLanguage()); +                    break; +                case 2: +                case 8: +                    NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); +                    break; +                case 3: +                case 12: +                    NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); +                    break; +                default: +                    return false; +            } +            return true; +        } +        else +        { +            Error error; +            uint8_t data_type = (process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1, 0, error) & 0x1F); +            uint64_t data_location = valobj_addr + 2*ptr_size; +            uint64_t value = 0; +            if (error.Fail()) +                return false; +            switch (data_type) +            { +                case 1: // 0B00001 +                    value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, error); +                    if (error.Fail()) +                        return false; +                    NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage()); +                    break; +                case 2: // 0B0010 +                    value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, error); +                    if (error.Fail()) +                        return false; +                    NSNumber_FormatShort(valobj, stream, (short)value, options.GetLanguage()); +                    break; +                case 3: // 0B0011 +                    value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error); +                    if (error.Fail()) +                        return false; +                    NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage()); +                    break; +                case 17: // 0B10001 +                    data_location += 8; +                case 4: // 0B0100 +                    value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error); +                    if (error.Fail()) +                        return false; +                    NSNumber_FormatLong(valobj, stream, value, options.GetLanguage()); +                    break; +                case 5: // 0B0101 +                { +                    uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error); +                    if (error.Fail()) +                        return false; +                    float flt_value = *((float*)&flt_as_int); +                    NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage()); +                    break; +                } +                case 6: // 0B0110 +                { +                    uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error); +                    if (error.Fail()) +                        return false; +                    double dbl_value = *((double*)&dbl_as_lng); +                    NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage()); +                    break; +                } +                default: +                    return false; +            } +            return true; +        } +    } +    else +    { +        return ExtractSummaryFromObjCExpression(valobj, "NSString*", "stringValue", stream, options.GetLanguage()); +    } +} + +bool +lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) +{ +    ProcessSP process_sp = valobj.GetProcessSP(); +    if (!process_sp) +        return false; +     +    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); +     +    if (!runtime) +        return false; +     +    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); +     +    if (!descriptor.get() || !descriptor->IsValid()) +        return false; +     +    uint32_t ptr_size = process_sp->GetAddressByteSize(); +     +    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); +     +    if (!valobj_addr) +        return false; +     +    const char* class_name = descriptor->GetClassName().GetCString(); +     +    if (!class_name || !*class_name) +        return false; +     +    if (strcmp(class_name, "NSURL") == 0) +    { +        uint64_t offset_text = ptr_size + ptr_size + 8; // ISA + pointer + 8 bytes of data (even on 32bit) +        uint64_t offset_base = offset_text + ptr_size; +        CompilerType type(valobj.GetCompilerType()); +        ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true)); +        ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true)); +        if (!text) +            return false; +        if (text->GetValueAsUnsigned(0) == 0) +            return false; +        StreamString summary; +        if (!NSStringSummaryProvider(*text, summary, options)) +            return false; +        if (base && base->GetValueAsUnsigned(0)) +        { +            if (summary.GetSize() > 0) +                summary.GetString().resize(summary.GetSize()-1); +            summary.Printf(" -- "); +            StreamString base_summary; +            if (NSURLSummaryProvider(*base, base_summary, options) && base_summary.GetSize() > 0) +                summary.Printf("%s",base_summary.GetSize() > 2 ? base_summary.GetData() + 2 : base_summary.GetData()); +        } +        if (summary.GetSize()) +        { +            stream.Printf("%s",summary.GetData()); +            return true; +        } +    } +    else +    { +        return ExtractSummaryFromObjCExpression(valobj, "NSString*", "description", stream, options.GetLanguage()); +    } +    return false; +} + +bool +lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) +{ +    ProcessSP process_sp = valobj.GetProcessSP(); +    if (!process_sp) +        return false; +     +    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); +     +    if (!runtime) +        return false; +     +    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); +     +    if (!descriptor.get() || !descriptor->IsValid()) +        return false; +     +    uint32_t ptr_size = process_sp->GetAddressByteSize(); +     +    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); +     +    if (!valobj_addr) +        return false; +     +    uint64_t date_value_bits = 0; +    double date_value = 0.0; +     +    const char* class_name = descriptor->GetClassName().GetCString(); +     +    if (!class_name || !*class_name) +        return false; +     +    if (strcmp(class_name,"NSDate") == 0 || +        strcmp(class_name,"__NSDate") == 0 || +        strcmp(class_name,"__NSTaggedDate") == 0) +    { +        uint64_t info_bits=0,value_bits = 0; +        if (descriptor->GetTaggedPointerInfo(&info_bits,&value_bits)) +        { +            date_value_bits = ((value_bits << 8) | (info_bits << 4)); +            date_value = *((double*)&date_value_bits); +        } +        else +        { +            Error error; +            date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 8, 0, error); +            date_value = *((double*)&date_value_bits); +            if (error.Fail()) +                return false; +        } +    } +    else if (!strcmp(class_name,"NSCalendarDate")) +    { +        Error error; +        date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, 8, 0, error); +        date_value = *((double*)&date_value_bits); +        if (error.Fail()) +            return false; +    } +    else +    { +        if (ExtractValueFromObjCExpression(valobj, "NSTimeInterval", "ExtractValueFromObjCExpression", date_value_bits) == false) +            return false; +        date_value = *((double*)&date_value_bits); +    } +    if (date_value == -63114076800) +    { +        stream.Printf("0001-12-30 00:00:00 +0000"); +        return true; +    } +    // this snippet of code assumes that time_t == seconds since Jan-1-1970 +    // this is generally true and POSIXly happy, but might break if a library +    // vendor decides to get creative +    time_t epoch = GetOSXEpoch(); +    epoch = epoch + (time_t)date_value; +    tm *tm_date = gmtime(&epoch); +    if (!tm_date) +        return false; +    std::string buffer(1024,0); +    if (strftime (&buffer[0], 1023, "%Z", tm_date) == 0) +        return false; +    stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year+1900, tm_date->tm_mon+1, tm_date->tm_mday, tm_date->tm_hour, tm_date->tm_min, tm_date->tm_sec, buffer.c_str()); +    return true; +} + +bool +lldb_private::formatters::ObjCClassSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) +{ +    ProcessSP process_sp = valobj.GetProcessSP(); +    if (!process_sp) +        return false; +     +    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); +     +    if (!runtime) +        return false; +     +    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0))); +     +    if (!descriptor.get() || !descriptor->IsValid()) +        return false; +     +    ConstString class_name = descriptor->GetClassName(); +     +    if (class_name.IsEmpty()) +        return false; +     +    if (ConstString cs = Mangled(class_name).GetDemangledName(lldb::eLanguageTypeUnknown)) +        class_name = cs; +     +    stream.Printf("%s",class_name.AsCString("<unknown class>")); +    return true; +} + +class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd +{ +public: +    ObjCClassSyntheticChildrenFrontEnd (lldb::ValueObjectSP valobj_sp) : +    SyntheticChildrenFrontEnd(*valobj_sp.get()) +    { +    } +     +    ~ObjCClassSyntheticChildrenFrontEnd() override = default; + +    size_t +    CalculateNumChildren() override +    { +        return 0; +    } +     +    lldb::ValueObjectSP +    GetChildAtIndex(size_t idx) override +    { +        return lldb::ValueObjectSP(); +    } +     +    bool +    Update() override +    { +        return false; +    } +     +    bool +    MightHaveChildren() override +    { +        return false; +    } +     +    size_t +    GetIndexOfChildWithName(const ConstString &name) override +    { +        return UINT32_MAX; +    } +}; + +SyntheticChildrenFrontEnd* +lldb_private::formatters::ObjCClassSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ +    return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp); +} + +template<bool needs_at> +bool +lldb_private::formatters::NSDataSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) +{ +    ProcessSP process_sp = valobj.GetProcessSP(); +    if (!process_sp) +        return false; +     +    ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); +     +    if (!runtime) +        return false; +     +    ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); +     +    if (!descriptor.get() || !descriptor->IsValid()) +        return false; +     +    bool is_64bit = (process_sp->GetAddressByteSize() == 8); +    lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); +     +    if (!valobj_addr) +        return false; +     +    uint64_t value = 0; +     +    const char* class_name = descriptor->GetClassName().GetCString(); +     +    if (!class_name || !*class_name) +        return false; +     +    if (!strcmp(class_name,"NSConcreteData") || +        !strcmp(class_name,"NSConcreteMutableData") || +        !strcmp(class_name,"__NSCFData")) +    { +        uint32_t offset = (is_64bit ? 16 : 8); +        Error error; +        value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, is_64bit ? 8 : 4, 0, error); +        if (error.Fail()) +            return false; +    } +    else +    { +        if (!ExtractValueFromObjCExpression(valobj, "int", "length", value)) +            return false; +    } +     +    stream.Printf("%s%" PRIu64 " byte%s%s", +                  (needs_at ? "@\"" : ""), +                  value, +                  (value != 1 ? "s" : ""), +                  (needs_at ? "\"" : "")); +     +    return true; +} + +bool +lldb_private::formatters::ObjCBOOLSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) +{ +    const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo(); +     +    ValueObjectSP real_guy_sp = valobj.GetSP(); +     +    if (type_info & eTypeIsPointer) +    { +        Error err; +        real_guy_sp = valobj.Dereference(err); +        if (err.Fail() || !real_guy_sp) +            return false; +    } +    else if (type_info & eTypeIsReference) +    { +        real_guy_sp =  valobj.GetChildAtIndex(0, true); +        if (!real_guy_sp) +            return false; +    } +    uint64_t value = real_guy_sp->GetValueAsUnsigned(0); +    if (value == 0) +    { +        stream.Printf("NO"); +        return true; +    } +    stream.Printf("YES"); +    return true; +} + +template <bool is_sel_ptr> +bool +lldb_private::formatters::ObjCSELSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) +{ +    lldb::ValueObjectSP valobj_sp; +     +    CompilerType charstar (valobj.GetCompilerType().GetBasicTypeFromAST(eBasicTypeChar).GetPointerType()); +     +    if (!charstar) +        return false; +     +    ExecutionContext exe_ctx(valobj.GetExecutionContextRef()); +     +    if (is_sel_ptr) +    { +        lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); +        if (data_address == LLDB_INVALID_ADDRESS) +            return false; +        valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address, exe_ctx, charstar); +    } +    else +    { +        DataExtractor data; +        Error error; +        valobj.GetData(data, error); +        if (error.Fail()) +            return false; +        valobj_sp = ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar); +    } +     +    if (!valobj_sp) +        return false; +     +    stream.Printf("%s",valobj_sp->GetSummaryAsCString()); +    return true; +} + +// POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-2001 +// this call gives the POSIX equivalent of the Cocoa epoch +time_t +lldb_private::formatters::GetOSXEpoch () +{ +    static time_t epoch = 0; +    if (!epoch) +    { +#ifndef _WIN32 +        tzset(); +        tm tm_epoch; +        tm_epoch.tm_sec = 0; +        tm_epoch.tm_hour = 0; +        tm_epoch.tm_min = 0; +        tm_epoch.tm_mon = 0; +        tm_epoch.tm_mday = 1; +        tm_epoch.tm_year = 2001-1900; // for some reason, we need to subtract 1900 from this field. not sure why. +        tm_epoch.tm_isdst = -1; +        tm_epoch.tm_gmtoff = 0; +        tm_epoch.tm_zone = NULL; +        epoch = timegm(&tm_epoch); +#endif +    } +    return epoch; +} + +bool +lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider (ValueObject& valobj, Stream& stream, const TypeSummaryOptions& options) +{ +    if (const char* description = valobj.GetObjectDescription()) +    { +        stream.Printf("%s", description); +        return true; +    } +    else +        return false; +} + +template bool +lldb_private::formatters::NSDataSummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&); + +template bool +lldb_private::formatters::NSDataSummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&); + +template bool +lldb_private::formatters::ObjCSELSummaryProvider<true> (ValueObject&, Stream&, const TypeSummaryOptions&); + +template bool +lldb_private::formatters::ObjCSELSummaryProvider<false> (ValueObject&, Stream&, const TypeSummaryOptions&);  | 
