brintos

brintos / llvm-project-archived public Read only

0
0
Text · 38.6 KiB · 72e4d0f Raw
1259 lines · cpp
1//===-- Cocoa.cpp ---------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "Cocoa.h"10#include "NSString.h"11#include "ObjCConstants.h"12 13#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"14#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"15#include "lldb/Core/Mangled.h"16#include "lldb/DataFormatters/FormattersHelpers.h"17#include "lldb/DataFormatters/StringPrinter.h"18#include "lldb/DataFormatters/TypeSummary.h"19#include "lldb/Host/Time.h"20#include "lldb/Target/Language.h"21#include "lldb/Target/Process.h"22#include "lldb/Target/Target.h"23#include "lldb/Utility/DataBufferHeap.h"24#include "lldb/Utility/Endian.h"25#include "lldb/Utility/LLDBLog.h"26#include "lldb/Utility/Status.h"27#include "lldb/Utility/Stream.h"28#include "lldb/ValueObject/ValueObject.h"29#include "lldb/ValueObject/ValueObjectConstResult.h"30 31#include "llvm/ADT/APInt.h"32#include "llvm/ADT/bit.h"33 34using namespace lldb;35using namespace lldb_private;36using namespace lldb_private::formatters;37 38bool lldb_private::formatters::NSBundleSummaryProvider(39    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {40  ProcessSP process_sp = valobj.GetProcessSP();41  if (!process_sp)42    return false;43 44  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);45 46  if (!runtime)47    return false;48 49  ObjCLanguageRuntime::ClassDescriptorSP descriptor(50      runtime->GetClassDescriptor(valobj));51 52  if (!descriptor || !descriptor->IsValid())53    return false;54 55  uint32_t ptr_size = process_sp->GetAddressByteSize();56 57  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);58 59  if (!valobj_addr)60    return false;61 62  llvm::StringRef class_name(descriptor->GetClassName().GetCString());63 64  if (class_name.empty())65    return false;66 67  if (class_name == "NSBundle") {68    uint64_t offset = 5 * ptr_size;69    ValueObjectSP text(valobj.GetSyntheticChildAtOffset(70        offset,71        valobj.GetCompilerType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID),72        true));73 74    if (!text)75      return false;76 77    StreamString summary_stream;78    bool was_nsstring_ok =79        NSStringSummaryProvider(*text, summary_stream, options);80    if (was_nsstring_ok && summary_stream.GetSize() > 0) {81      stream.Printf("%s", summary_stream.GetData());82      return true;83    }84  }85 86  return false;87}88 89bool lldb_private::formatters::NSTimeZoneSummaryProvider(90    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {91  ProcessSP process_sp = valobj.GetProcessSP();92  if (!process_sp)93    return false;94 95  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);96 97  if (!runtime)98    return false;99 100  ObjCLanguageRuntime::ClassDescriptorSP descriptor(101      runtime->GetClassDescriptor(valobj));102 103  if (!descriptor || !descriptor->IsValid())104    return false;105 106  uint32_t ptr_size = process_sp->GetAddressByteSize();107 108  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);109 110  if (!valobj_addr)111    return false;112 113  llvm::StringRef class_name(descriptor->GetClassName().GetCString());114 115  if (class_name.empty())116    return false;117 118  if (class_name == "__NSTimeZone") {119    uint64_t offset = ptr_size;120    ValueObjectSP text(valobj.GetSyntheticChildAtOffset(121        offset, valobj.GetCompilerType(), true));122 123    if (!text)124      return false;125 126    StreamString summary_stream;127    bool was_nsstring_ok =128        NSStringSummaryProvider(*text, summary_stream, options);129    if (was_nsstring_ok && summary_stream.GetSize() > 0) {130      stream.Printf("%s", summary_stream.GetData());131      return true;132    }133  }134 135  return false;136}137 138bool lldb_private::formatters::NSNotificationSummaryProvider(139    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {140  ProcessSP process_sp = valobj.GetProcessSP();141  if (!process_sp)142    return false;143 144  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);145 146  if (!runtime)147    return false;148 149  ObjCLanguageRuntime::ClassDescriptorSP descriptor(150      runtime->GetClassDescriptor(valobj));151 152  if (!descriptor || !descriptor->IsValid())153    return false;154 155  uint32_t ptr_size = process_sp->GetAddressByteSize();156 157  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);158 159  if (!valobj_addr)160    return false;161 162  llvm::StringRef class_name(descriptor->GetClassName().GetCString());163 164  if (class_name.empty())165    return false;166 167  if (class_name == "NSConcreteNotification") {168    uint64_t offset = ptr_size;169    ValueObjectSP text(valobj.GetSyntheticChildAtOffset(170        offset, valobj.GetCompilerType(), true));171 172    if (!text)173      return false;174 175    StreamString summary_stream;176    bool was_nsstring_ok =177        NSStringSummaryProvider(*text, summary_stream, options);178    if (was_nsstring_ok && summary_stream.GetSize() > 0) {179      stream.Printf("%s", summary_stream.GetData());180      return true;181    }182  }183 184  return false;185}186 187bool lldb_private::formatters::NSMachPortSummaryProvider(188    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {189  ProcessSP process_sp = valobj.GetProcessSP();190  if (!process_sp)191    return false;192 193  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);194 195  if (!runtime)196    return false;197 198  ObjCLanguageRuntime::ClassDescriptorSP descriptor(199      runtime->GetClassDescriptor(valobj));200 201  if (!descriptor || !descriptor->IsValid())202    return false;203 204  uint32_t ptr_size = process_sp->GetAddressByteSize();205 206  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);207 208  if (!valobj_addr)209    return false;210 211  llvm::StringRef class_name(descriptor->GetClassName().GetCString());212 213  if (class_name.empty())214    return false;215 216  uint64_t port_number = 0;217 218  if (class_name == "NSMachPort") {219    uint64_t offset = (ptr_size == 4 ? 12 : 20);220    Status error;221    port_number = process_sp->ReadUnsignedIntegerFromMemory(222        offset + valobj_addr, 4, 0, error);223    if (error.Success()) {224      stream.Printf("mach port: %u",225                    (uint32_t)(port_number & 0x00000000FFFFFFFF));226      return true;227    }228  }229 230  return false;231}232 233bool lldb_private::formatters::NSIndexSetSummaryProvider(234    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {235  ProcessSP process_sp = valobj.GetProcessSP();236  if (!process_sp)237    return false;238 239  AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(240      ObjCLanguageRuntime::Get(*process_sp));241 242  if (!runtime)243    return false;244 245  ObjCLanguageRuntime::ClassDescriptorSP descriptor(246      runtime->GetClassDescriptor(valobj));247 248  if (!descriptor || !descriptor->IsValid())249    return false;250 251  uint32_t ptr_size = process_sp->GetAddressByteSize();252 253  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);254 255  if (!valobj_addr)256    return false;257 258  llvm::StringRef class_name(descriptor->GetClassName().GetCString());259 260  if (class_name.empty())261    return false;262 263  uint64_t count = 0;264 265  do {266    if (class_name == "NSIndexSet" || class_name == "NSMutableIndexSet") {267      // Foundation version 2000 added a bitmask if the index set fit in 64 bits268      // and a Tagged Pointer version if the bitmask is small enough to fit in269      // the tagged pointer payload.270      // It also changed the layout (but not the size) of the set descriptor.271 272      // First check whether this is a tagged pointer.  The bitmask will be in273      // the payload of the tagged pointer.274      uint64_t payload;275      if (runtime->GetFoundationVersion() >= 2000 &&276          descriptor->GetTaggedPointerInfo(nullptr, nullptr, &payload)) {277        count = llvm::popcount(payload);278        break;279      }280      // The first 32 bits describe the index set in all cases:281      Status error;282      uint32_t mode = process_sp->ReadUnsignedIntegerFromMemory(283          valobj_addr + ptr_size, 4, 0, error);284      if (error.Fail())285        return false;286      // Now check if the index is held in a bitmask in the object:287      if (runtime->GetFoundationVersion() >= 2000) {288        // The first two bits are "isSingleRange" and "isBitfield".  If this is289        // a bitfield we handle it here, otherwise set mode appropriately and290        // the rest of the treatment is in common.291        if ((mode & 2) == 2) {292          // The bitfield is a 64 bit uint at the beginning of the data var.293          uint64_t bitfield = process_sp->ReadUnsignedIntegerFromMemory(294              valobj_addr + 2 * ptr_size, 8, 0, error);295          if (error.Fail())296            return false;297          count = llvm::popcount(bitfield);298          break;299        }300        // It wasn't a bitfield, so read the isSingleRange from its new loc:301        if ((mode & 1) == 1)302          mode = 1; // this means the set only has one range303        else304          mode = 2; // this means the set has multiple ranges305      } else {306        // this means the set is empty - count = 0307        if ((mode & 1) == 1) {308          count = 0;309          break;310        }311 312        if ((mode & 2) == 2)313          mode = 1; // this means the set only has one range314        else315          mode = 2; // this means the set has multiple ranges316      }317      if (mode == 1) {318        count = process_sp->ReadUnsignedIntegerFromMemory(319            valobj_addr + 3 * ptr_size, ptr_size, 0, error);320        if (error.Fail())321          return false;322      } else {323        // read a pointer to the data at 2*ptr_size324        count = process_sp->ReadUnsignedIntegerFromMemory(325            valobj_addr + 2 * ptr_size, ptr_size, 0, error);326        if (error.Fail())327          return false;328        // read the data at 2*ptr_size from the first location329        count = process_sp->ReadUnsignedIntegerFromMemory(count + 2 * ptr_size,330                                                          ptr_size, 0, error);331        if (error.Fail())332          return false;333      }334    } else335      return false;336  } while (false);337  stream.Printf("%" PRIu64 " index%s", count, (count == 1 ? "" : "es"));338  return true;339}340 341static void NSNumber_FormatChar(ValueObject &valobj, Stream &stream, char value,342                                lldb::LanguageType lang) {343  static constexpr llvm::StringLiteral g_TypeHint("NSNumber:char");344 345  llvm::StringRef prefix, suffix;346  if (Language *language = Language::FindPlugin(lang))347    std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);348 349  stream << prefix;350  stream.Printf("%hhd", value);351  stream << suffix;352}353 354static void NSNumber_FormatShort(ValueObject &valobj, Stream &stream,355                                 short value, lldb::LanguageType lang) {356  static constexpr llvm::StringLiteral g_TypeHint("NSNumber:short");357 358  llvm::StringRef prefix, suffix;359  if (Language *language = Language::FindPlugin(lang))360    std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);361 362  stream << prefix;363  stream.Printf("%hd", value);364  stream << suffix;365}366 367static void NSNumber_FormatInt(ValueObject &valobj, Stream &stream, int value,368                               lldb::LanguageType lang) {369  static constexpr llvm::StringLiteral g_TypeHint("NSNumber:int");370 371  llvm::StringRef prefix, suffix;372  if (Language *language = Language::FindPlugin(lang))373    std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);374 375  stream << prefix;376  stream.Printf("%d", value);377  stream << suffix;378}379 380static void NSNumber_FormatLong(ValueObject &valobj, Stream &stream,381                                int64_t value, lldb::LanguageType lang) {382  static constexpr llvm::StringLiteral g_TypeHint("NSNumber:long");383 384  llvm::StringRef prefix, suffix;385  if (Language *language = Language::FindPlugin(lang))386    std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);387 388  stream << prefix;389  stream.Printf("%" PRId64 "", value);390  stream << suffix;391}392 393static void NSNumber_FormatInt128(ValueObject &valobj, Stream &stream,394                                  const llvm::APInt &value,395                                  lldb::LanguageType lang) {396  static constexpr llvm::StringLiteral g_TypeHint("NSNumber:int128_t");397 398  llvm::StringRef prefix, suffix;399  if (Language *language = Language::FindPlugin(lang))400    std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);401 402  stream << prefix;403  const int radix = 10;404  const bool isSigned = true;405  std::string str = llvm::toString(value, radix, isSigned);406  stream.PutCString(str.c_str());407  stream << suffix;408}409 410static void NSNumber_FormatFloat(ValueObject &valobj, Stream &stream,411                                 float value, lldb::LanguageType lang) {412  static constexpr llvm::StringLiteral g_TypeHint("NSNumber:float");413 414  llvm::StringRef prefix, suffix;415  if (Language *language = Language::FindPlugin(lang))416    std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);417 418  stream << prefix;419  stream.Printf("%f", value);420  stream << suffix;421}422 423static void NSNumber_FormatDouble(ValueObject &valobj, Stream &stream,424                                  double value, lldb::LanguageType lang) {425  static constexpr llvm::StringLiteral g_TypeHint("NSNumber:double");426 427  llvm::StringRef prefix, suffix;428  if (Language *language = Language::FindPlugin(lang))429    std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);430 431  stream << prefix;432  stream.Printf("%g", value);433  stream << suffix;434}435 436bool lldb_private::formatters::NSNumberSummaryProvider(437    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {438  ProcessSP process_sp = valobj.GetProcessSP();439  if (!process_sp)440    return false;441 442  Log *log = GetLog(LLDBLog::DataFormatters);443  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);444 445  if (!runtime)446    return false;447 448  ObjCLanguageRuntime::ClassDescriptorSP descriptor(449      runtime->GetClassDescriptor(valobj));450 451  if (!descriptor || !descriptor->IsValid())452    return false;453 454  uint32_t ptr_size = process_sp->GetAddressByteSize();455 456  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);457 458  if (!valobj_addr)459    return false;460 461  llvm::StringRef class_name(descriptor->GetClassName().GetCString());462 463  if (class_name.empty())464    return false;465 466  if (class_name == "__NSCFBoolean")467    return ObjCBooleanSummaryProvider(valobj, stream, options);468 469  if (class_name == "NSDecimalNumber")470    return NSDecimalNumberSummaryProvider(valobj, stream, options);471 472  if (class_name == "NSConstantIntegerNumber") {473    Status error;474    int64_t value = process_sp->ReadSignedIntegerFromMemory(475        valobj_addr + 2 * ptr_size, 8, 0, error);476    if (error.Fail())477      return false;478    uint64_t encoding_addr = process_sp->ReadUnsignedIntegerFromMemory(479        valobj_addr + ptr_size, ptr_size, 0, error);480    if (error.Fail())481      return false;482    char encoding =483        process_sp->ReadUnsignedIntegerFromMemory(encoding_addr, 1, 0, error);484    if (error.Fail())485      return false;486 487    switch (encoding) {488    case _C_CHR:489      NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());490      return true;491    case _C_SHT:492      NSNumber_FormatShort(valobj, stream, (short)value, options.GetLanguage());493      return true;494    case _C_INT:495      NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());496      return true;497    case _C_LNG:498    case _C_LNG_LNG:499      NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());500      return true;501 502    case _C_UCHR:503    case _C_USHT:504    case _C_UINT:505    case _C_ULNG:506    case _C_ULNG_LNG:507      stream.Printf("%" PRIu64, value);508      return true;509    }510 511    return false;512  }513 514  if (class_name == "NSConstantFloatNumber") {515    Status error;516    uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(517        valobj_addr + ptr_size, 4, 0, error);518    if (error.Fail())519      return false;520    float flt_value = 0.0f;521    memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int));522    NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage());523    return true;524  }525 526  if (class_name == "NSConstantDoubleNumber") {527    Status error;528    uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(529        valobj_addr + ptr_size, 8, 0, error);530    if (error.Fail())531      return false;532    double dbl_value = 0.0;533    memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng));534    NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage());535    return true;536  }537 538  if (class_name == "NSNumber" || class_name == "__NSCFNumber") {539    int64_t value = 0;540    uint64_t i_bits = 0;541    if (descriptor->GetTaggedPointerInfoSigned(&i_bits, &value)) {542      // Check for "preserved" numbers.  We still don't support them yet.543      if (i_bits & 0x8) {544        if (log)545          log->Printf(546              "Unsupported (preserved) NSNumber tagged pointer 0x%" PRIu64,547              valobj_addr);548        return false;549      }550 551      switch (i_bits) {552      case 0:553        NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());554        break;555      case 1:556      case 4:557        NSNumber_FormatShort(valobj, stream, (short)value,558                             options.GetLanguage());559        break;560      case 2:561      case 8:562        NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());563        break;564      case 3:565      case 12:566        NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());567        break;568      default:569        return false;570      }571      return true;572    } else {573      Status error;574 575      AppleObjCRuntime *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(576          ObjCLanguageRuntime::Get(*process_sp));577 578      const bool new_format =579          (runtime && runtime->GetFoundationVersion() >= 1400);580 581      enum class TypeCodes : int {582        sint8 = 0x0,583        sint16 = 0x1,584        sint32 = 0x2,585        sint64 = 0x3,586        f32 = 0x4,587        f64 = 0x5,588        sint128 = 0x6589      };590 591      uint64_t data_location = valobj_addr + 2 * ptr_size;592      TypeCodes type_code;593 594      if (new_format) {595        uint64_t cfinfoa = process_sp->ReadUnsignedIntegerFromMemory(596            valobj_addr + ptr_size, ptr_size, 0, error);597 598        if (error.Fail())599          return false;600 601        bool is_preserved_number = cfinfoa & 0x8;602        if (is_preserved_number) {603          if (log)604            log->Printf(605                "Unsupported preserved NSNumber tagged pointer 0x%" PRIu64,606                valobj_addr);607          return false;608        }609 610        type_code = static_cast<TypeCodes>(cfinfoa & 0x7);611      } else {612        uint8_t data_type = process_sp->ReadUnsignedIntegerFromMemory(613                                valobj_addr + ptr_size, 1, 0, error) &614                            0x1F;615 616        if (error.Fail())617          return false;618 619        switch (data_type) {620        case 1:621          type_code = TypeCodes::sint8;622          break;623        case 2:624          type_code = TypeCodes::sint16;625          break;626        case 3:627          type_code = TypeCodes::sint32;628          break;629        case 17:630          data_location += 8;631          [[fallthrough]];632        case 4:633          type_code = TypeCodes::sint64;634          break;635        case 5:636          type_code = TypeCodes::f32;637          break;638        case 6:639          type_code = TypeCodes::f64;640          break;641        default:642          return false;643        }644      }645 646      uint64_t value = 0;647      bool success = false;648      switch (type_code) {649      case TypeCodes::sint8:650        value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0,651                                                          error);652        if (error.Fail())653          return false;654        NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());655        success = true;656        break;657      case TypeCodes::sint16:658        value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0,659                                                          error);660        if (error.Fail())661          return false;662        NSNumber_FormatShort(valobj, stream, (short)value,663                             options.GetLanguage());664        success = true;665        break;666      case TypeCodes::sint32:667        value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0,668                                                          error);669        if (error.Fail())670          return false;671        NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());672        success = true;673        break;674      case TypeCodes::sint64:675        value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0,676                                                          error);677        if (error.Fail())678          return false;679        NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());680        success = true;681        break;682      case TypeCodes::f32: {683        uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(684            data_location, 4, 0, error);685        if (error.Fail())686          return false;687        float flt_value = 0.0f;688        memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int));689        NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage());690        success = true;691        break;692      }693      case TypeCodes::f64: {694        uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(695            data_location, 8, 0, error);696        if (error.Fail())697          return false;698        double dbl_value = 0.0;699        memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng));700        NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage());701        success = true;702        break;703      }704      case TypeCodes::sint128: // internally, this is the same705      {706        uint64_t words[2];707        words[1] = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8,708                                                             0, error);709        if (error.Fail())710          return false;711        words[0] = process_sp->ReadUnsignedIntegerFromMemory(data_location + 8,712                                                             8, 0, error);713        if (error.Fail())714          return false;715        llvm::APInt i128_value(128, words);716        NSNumber_FormatInt128(valobj, stream, i128_value,717                              options.GetLanguage());718        success = true;719        break;720      }721      }722      return success;723    }724  }725 726  return false;727}728 729bool lldb_private::formatters::NSDecimalNumberSummaryProvider(730    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {731  ProcessSP process_sp = valobj.GetProcessSP();732  if (!process_sp)733    return false;734 735  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);736  uint32_t ptr_size = process_sp->GetAddressByteSize();737 738  Status error;739  int8_t exponent = process_sp->ReadUnsignedIntegerFromMemory(740      valobj_addr + ptr_size, 1, 0, error);741  if (error.Fail())742    return false;743 744  uint8_t length_and_negative = process_sp->ReadUnsignedIntegerFromMemory(745      valobj_addr + ptr_size + 1, 1, 0, error);746  if (error.Fail())747    return false;748 749  // Fifth bit marks negativity.750  const bool is_negative = (length_and_negative >> 4) & 1;751 752  // Zero length and negative means NaN.753  uint8_t length = length_and_negative & 0xf;754  const bool is_nan = is_negative && (length == 0);755 756  if (is_nan) {757    stream.Printf("NaN");758    return true;759  }760 761  if (length == 0) {762    stream.Printf("0");763    return true;764  }765 766  uint64_t mantissa = process_sp->ReadUnsignedIntegerFromMemory(767      valobj_addr + ptr_size + 4, 8, 0, error);768  if (error.Fail())769    return false;770 771  if (is_negative)772    stream.Printf("-");773 774  stream.Printf("%" PRIu64 " x 10^%" PRIi8, mantissa, exponent);775  return true;776}777 778bool lldb_private::formatters::NSURLSummaryProvider(779    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {780  ProcessSP process_sp = valobj.GetProcessSP();781  if (!process_sp)782    return false;783 784  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);785 786  if (!runtime)787    return false;788 789  ObjCLanguageRuntime::ClassDescriptorSP descriptor(790      runtime->GetClassDescriptor(valobj));791 792  if (!descriptor || !descriptor->IsValid())793    return false;794 795  uint32_t ptr_size = process_sp->GetAddressByteSize();796 797  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);798 799  if (!valobj_addr)800    return false;801 802  llvm::StringRef class_name = descriptor->GetClassName().GetStringRef();803 804  if (class_name != "NSURL")805    return false;806 807  uint64_t offset_text = ptr_size + ptr_size +808                         8; // ISA + pointer + 8 bytes of data (even on 32bit)809  uint64_t offset_base = offset_text + ptr_size;810  CompilerType type(valobj.GetCompilerType());811  ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset_text, type, true));812  ValueObjectSP base(valobj.GetSyntheticChildAtOffset(offset_base, type, true));813  if (!text || text->GetValueAsUnsigned(0) == 0)814    return false;815 816  StreamString base_summary;817  if (base && base->GetValueAsUnsigned(0)) {818    if (!NSURLSummaryProvider(*base, base_summary, options))819      base_summary.Clear();820  }821  if (base_summary.Empty())822    return NSStringSummaryProvider(*text, stream, options);823 824  StreamString summary;825  if (!NSStringSummaryProvider(*text, summary, options) || summary.Empty())826    return false;827 828  static constexpr llvm::StringLiteral quote_char("\"");829  static constexpr llvm::StringLiteral g_TypeHint("NSString");830  llvm::StringRef prefix, suffix;831  if (Language *language = Language::FindPlugin(options.GetLanguage()))832    std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);833 834  // @"A" -> @"A835  llvm::StringRef summary_str = summary.GetString();836  bool back_consumed =837      summary_str.consume_back(suffix) && summary_str.consume_back(quote_char);838  assert(back_consumed);839  UNUSED_IF_ASSERT_DISABLED(back_consumed);840  // @"B" -> B"841  llvm::StringRef base_summary_str = base_summary.GetString();842  bool front_consumed = base_summary_str.consume_front(prefix) &&843                        base_summary_str.consume_front(quote_char);844  assert(front_consumed);845  UNUSED_IF_ASSERT_DISABLED(front_consumed);846  // @"A -- B"847  if (!summary_str.empty() && !base_summary_str.empty()) {848    stream << summary_str << " -- " << base_summary_str;849    return true;850  }851 852  return false;853}854 855/// Bias value for tagged pointer exponents.856/// Recommended values:857/// 0x3e3: encodes all dates between distantPast and distantFuture858///   except for the range within about 1e-28 second of the reference date.859/// 0x3ef: encodes all dates for a few million years beyond distantPast and860///   distantFuture, except within about 1e-25 second of the reference date.861const int TAGGED_DATE_EXPONENT_BIAS = 0x3ef;862 863struct DoubleBits {864  uint64_t fraction : 52; // unsigned865  uint64_t exponent : 11; // signed866  uint64_t sign : 1;867};868 869struct TaggedDoubleBits {870  uint64_t fraction : 52; // unsigned871  uint64_t exponent : 7;  // signed872  uint64_t sign : 1;873  uint64_t unused : 4; // placeholder for pointer tag bits874};875 876static uint64_t decodeExponent(uint64_t exp) {877  // Tagged exponent field is 7-bit signed. Sign-extend the value to 64 bits878  // before performing arithmetic.879  return llvm::SignExtend64<7>(exp) + TAGGED_DATE_EXPONENT_BIAS;880}881 882static double decodeTaggedTimeInterval(uint64_t encodedTimeInterval) {883  if (encodedTimeInterval == 0)884    return 0.0;885  if (encodedTimeInterval == std::numeric_limits<uint64_t>::max())886    return (uint64_t)-0.0;887 888  TaggedDoubleBits encodedBits =889      llvm::bit_cast<TaggedDoubleBits>(encodedTimeInterval);890  assert(encodedBits.unused == 0);891 892  // Sign and fraction are represented exactly.893  // Exponent is encoded.894  DoubleBits decodedBits;895  decodedBits.sign = encodedBits.sign;896  decodedBits.fraction = encodedBits.fraction;897  decodedBits.exponent = decodeExponent(encodedBits.exponent);898 899  return llvm::bit_cast<double>(decodedBits);900}901 902bool lldb_private::formatters::NSDateSummaryProvider(903    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {904  ProcessSP process_sp = valobj.GetProcessSP();905  if (!process_sp)906    return false;907 908  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);909 910  if (!runtime)911    return false;912 913  ObjCLanguageRuntime::ClassDescriptorSP descriptor(914      runtime->GetClassDescriptor(valobj));915 916  if (!descriptor || !descriptor->IsValid())917    return false;918 919  uint32_t ptr_size = process_sp->GetAddressByteSize();920 921  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);922 923  if (!valobj_addr)924    return false;925 926  uint64_t date_value_bits = 0;927  double date_value = 0.0;928 929  ConstString class_name = descriptor->GetClassName();930 931  static const ConstString g_NSDate("NSDate");932  static const ConstString g_dunder_NSDate("__NSDate");933  static const ConstString g_NSTaggedDate("__NSTaggedDate");934  static const ConstString g_NSCalendarDate("NSCalendarDate");935  static const ConstString g_NSConstantDate("NSConstantDate");936 937  if (class_name.IsEmpty())938    return false;939 940  uint64_t info_bits = 0, value_bits = 0;941  if ((class_name == g_NSDate) || (class_name == g_dunder_NSDate) ||942      (class_name == g_NSTaggedDate) || (class_name == g_NSConstantDate)) {943    if (descriptor->GetTaggedPointerInfo(&info_bits, &value_bits)) {944      date_value_bits = ((value_bits << 8) | (info_bits << 4));945      memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));946    } else {947      llvm::Triple triple(948          process_sp->GetTarget().GetArchitecture().GetTriple());949      uint32_t delta =950          (triple.isWatchOS() && triple.isWatchABI()) ? 8 : ptr_size;951      Status error;952      date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(953          valobj_addr + delta, 8, 0, error);954      memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));955      if (error.Fail())956        return false;957    }958  } else if (class_name == g_NSCalendarDate) {959    Status error;960    date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(961        valobj_addr + 2 * ptr_size, 8, 0, error);962    memcpy(&date_value, &date_value_bits, sizeof(date_value_bits));963    if (error.Fail())964      return false;965  } else966    return false;967 968  // FIXME: It seems old dates are not formatted according to NSDate's calendar969  // so we hardcode distantPast's value so that it looks like LLDB is doing970  // the right thing.971 972  // The relative time in seconds from Cocoa Epoch to [NSDate distantPast].973  const double RelSecondsFromCocoaEpochToNSDateDistantPast = -63114076800;974  if (date_value == RelSecondsFromCocoaEpochToNSDateDistantPast) {975    stream.Printf("0001-01-01 00:00:00 UTC");976    return true;977  }978 979  // Accomodate for the __NSTaggedDate format introduced in Foundation 1600.980  if (class_name == g_NSTaggedDate) {981    auto *runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(982        ObjCLanguageRuntime::Get(*process_sp));983    if (runtime && runtime->GetFoundationVersion() >= 1600)984      date_value = decodeTaggedTimeInterval(value_bits << 4);985  }986 987  // this snippet of code assumes that time_t == seconds since Jan-1-1970 this988  // is generally true and POSIXly happy, but might break if a library vendor989  // decides to get creative990  time_t epoch = GetOSXEpoch();991  epoch = epoch + static_cast<time_t>(std::floor(date_value));992  tm *tm_date = gmtime(&epoch);993  if (!tm_date)994    return false;995  std::string buffer(1024, 0);996  if (strftime(&buffer[0], 1023, "%Z", tm_date) == 0)997    return false;998  stream.Printf("%04d-%02d-%02d %02d:%02d:%02d %s", tm_date->tm_year + 1900,999                tm_date->tm_mon + 1, tm_date->tm_mday, tm_date->tm_hour,1000                tm_date->tm_min, tm_date->tm_sec, buffer.c_str());1001  return true;1002}1003 1004bool lldb_private::formatters::ObjCClassSummaryProvider(1005    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {1006  ProcessSP process_sp = valobj.GetProcessSP();1007  if (!process_sp)1008    return false;1009 1010  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);1011 1012  if (!runtime)1013    return false;1014 1015  ObjCLanguageRuntime::ClassDescriptorSP descriptor(1016      runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0)));1017 1018  if (!descriptor || !descriptor->IsValid())1019    return false;1020 1021  ConstString class_name = descriptor->GetClassName();1022 1023  if (class_name.IsEmpty())1024    return false;1025 1026  if (ConstString cs = Mangled(class_name).GetDemangledName())1027    class_name = cs;1028 1029  stream.Printf("%s", class_name.AsCString("<unknown class>"));1030  return true;1031}1032 1033class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd {1034public:1035  ObjCClassSyntheticChildrenFrontEnd(lldb::ValueObjectSP valobj_sp)1036      : SyntheticChildrenFrontEnd(*valobj_sp) {}1037 1038  ~ObjCClassSyntheticChildrenFrontEnd() override = default;1039 1040  llvm::Expected<uint32_t> CalculateNumChildren() override { return 0; }1041 1042  lldb::ValueObjectSP GetChildAtIndex(uint32_t idx) override {1043    return lldb::ValueObjectSP();1044  }1045 1046  lldb::ChildCacheState Update() override {1047    return lldb::ChildCacheState::eRefetch;1048  }1049 1050  bool MightHaveChildren() override { return false; }1051 1052  llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override {1053    return llvm::createStringError("Type has no child named '%s'",1054                                   name.AsCString());1055  }1056};1057 1058SyntheticChildrenFrontEnd *1059lldb_private::formatters::ObjCClassSyntheticFrontEndCreator(1060    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {1061  return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp);1062}1063 1064template <bool needs_at>1065bool lldb_private::formatters::NSDataSummaryProvider(1066    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {1067  ProcessSP process_sp = valobj.GetProcessSP();1068  if (!process_sp)1069    return false;1070 1071  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);1072 1073  if (!runtime)1074    return false;1075 1076  ObjCLanguageRuntime::ClassDescriptorSP descriptor(1077      runtime->GetClassDescriptor(valobj));1078 1079  if (!descriptor || !descriptor->IsValid())1080    return false;1081 1082  bool is_64bit = (process_sp->GetAddressByteSize() == 8);1083  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);1084 1085  if (!valobj_addr)1086    return false;1087 1088  uint64_t value = 0;1089 1090  llvm::StringRef class_name = descriptor->GetClassName().GetCString();1091 1092  if (class_name.empty())1093    return false;1094 1095  bool isNSConcreteData = class_name == "NSConcreteData";1096  bool isNSConcreteMutableData = class_name == "NSConcreteMutableData";1097  bool isNSCFData = class_name == "__NSCFData";1098  if (isNSConcreteData || isNSConcreteMutableData || isNSCFData) {1099    uint32_t offset;1100    if (isNSConcreteData)1101      offset = is_64bit ? 8 : 4;1102    else1103      offset = is_64bit ? 16 : 8;1104 1105    Status error;1106    value = process_sp->ReadUnsignedIntegerFromMemory(1107        valobj_addr + offset, is_64bit ? 8 : 4, 0, error);1108    if (error.Fail())1109      return false;1110  } else if (class_name == "_NSInlineData") {1111    uint32_t offset = (is_64bit ? 8 : 4);1112    Status error;1113    value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, 2,1114                                                      0, error);1115    if (error.Fail())1116      return false;1117  } else if (class_name == "_NSZeroData") {1118    value = 0;1119  } else1120    return false;1121 1122  stream.Printf("%s%" PRIu64 " byte%s%s", (needs_at ? "@\"" : ""), value,1123                (value != 1 ? "s" : ""), (needs_at ? "\"" : ""));1124 1125  return true;1126}1127 1128bool lldb_private::formatters::ObjCBOOLSummaryProvider(1129    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {1130  const uint32_t type_info = valobj.GetCompilerType().GetTypeInfo();1131 1132  ValueObjectSP real_guy_sp = valobj.GetSP();1133 1134  if (type_info & eTypeIsPointer) {1135    Status err;1136    real_guy_sp = valobj.Dereference(err);1137    if (err.Fail() || !real_guy_sp)1138      return false;1139  } else if (type_info & eTypeIsReference) {1140    real_guy_sp = valobj.GetChildAtIndex(0);1141    if (!real_guy_sp)1142      return false;1143  }1144  int8_t value = (real_guy_sp->GetValueAsSigned(0) & 0xFF);1145  switch (value) {1146  case 0:1147    stream.Printf("NO");1148    break;1149  case 1:1150    stream.Printf("YES");1151    break;1152  default:1153    stream.Printf("%d", value);1154    break;1155  }1156  return true;1157}1158 1159bool lldb_private::formatters::ObjCBooleanSummaryProvider(1160    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {1161  lldb::addr_t valobj_ptr_value =1162      valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);1163  if (valobj_ptr_value == LLDB_INVALID_ADDRESS)1164    return false;1165 1166  ProcessSP process_sp(valobj.GetProcessSP());1167  if (!process_sp)1168    return false;1169 1170  if (AppleObjCRuntime *objc_runtime = llvm::dyn_cast_or_null<AppleObjCRuntime>(1171          ObjCLanguageRuntime::Get(*process_sp))) {1172    lldb::addr_t cf_true = LLDB_INVALID_ADDRESS,1173                 cf_false = LLDB_INVALID_ADDRESS;1174    objc_runtime->GetValuesForGlobalCFBooleans(cf_true, cf_false);1175    if (valobj_ptr_value == cf_true) {1176      stream.PutCString("YES");1177      return true;1178    }1179    if (valobj_ptr_value == cf_false) {1180      stream.PutCString("NO");1181      return true;1182    }1183  }1184 1185  return false;1186}1187 1188template <bool is_sel_ptr>1189bool lldb_private::formatters::ObjCSELSummaryProvider(1190    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {1191  lldb::ValueObjectSP valobj_sp;1192 1193  CompilerType charstar(valobj.GetCompilerType()1194                            .GetBasicTypeFromAST(eBasicTypeChar)1195                            .GetPointerType());1196 1197  if (!charstar)1198    return false;1199 1200  ExecutionContext exe_ctx(valobj.GetExecutionContextRef());1201 1202  if (is_sel_ptr) {1203    lldb::addr_t data_address = valobj.GetValueAsUnsigned(LLDB_INVALID_ADDRESS);1204    if (data_address == LLDB_INVALID_ADDRESS)1205      return false;1206    valobj_sp = ValueObject::CreateValueObjectFromAddress("text", data_address,1207                                                          exe_ctx, charstar);1208  } else {1209    DataExtractor data;1210    Status error;1211    valobj.GetData(data, error);1212    if (error.Fail())1213      return false;1214    valobj_sp =1215        ValueObject::CreateValueObjectFromData("text", data, exe_ctx, charstar);1216  }1217 1218  if (!valobj_sp)1219    return false;1220 1221  stream.Printf("%s", valobj_sp->GetSummaryAsCString());1222  return true;1223}1224 1225// POSIX has an epoch on Jan-1-1970, but Cocoa prefers Jan-1-20011226// this call gives the POSIX equivalent of the Cocoa epoch1227time_t lldb_private::formatters::GetOSXEpoch() {1228  static time_t epoch = 0;1229  if (!epoch) {1230#if !defined(_WIN32) && !defined(_AIX)1231    tzset();1232    tm tm_epoch;1233    tm_epoch.tm_sec = 0;1234    tm_epoch.tm_hour = 0;1235    tm_epoch.tm_min = 0;1236    tm_epoch.tm_mon = 0;1237    tm_epoch.tm_mday = 1;1238    tm_epoch.tm_year = 2001 - 1900;1239    tm_epoch.tm_isdst = -1;1240    tm_epoch.tm_gmtoff = 0;1241    tm_epoch.tm_zone = nullptr;1242    epoch = timegm(&tm_epoch);1243#endif1244  }1245  return epoch;1246}1247 1248template bool lldb_private::formatters::NSDataSummaryProvider<true>(1249    ValueObject &, Stream &, const TypeSummaryOptions &);1250 1251template bool lldb_private::formatters::NSDataSummaryProvider<false>(1252    ValueObject &, Stream &, const TypeSummaryOptions &);1253 1254template bool lldb_private::formatters::ObjCSELSummaryProvider<true>(1255    ValueObject &, Stream &, const TypeSummaryOptions &);1256 1257template bool lldb_private::formatters::ObjCSELSummaryProvider<false>(1258    ValueObject &, Stream &, const TypeSummaryOptions &);1259