brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.3 KiB · 2626b9a Raw
410 lines · cpp
1//===-- NSString.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 "NSString.h"10 11#include "lldb/DataFormatters/FormattersHelpers.h"12#include "lldb/DataFormatters/StringPrinter.h"13#include "lldb/Target/Language.h"14#include "lldb/Target/Target.h"15#include "lldb/Utility/ConstString.h"16#include "lldb/Utility/DataBufferHeap.h"17#include "lldb/Utility/Endian.h"18#include "lldb/Utility/Status.h"19#include "lldb/Utility/Stream.h"20#include "lldb/ValueObject/ValueObject.h"21#include "lldb/ValueObject/ValueObjectConstResult.h"22 23using namespace lldb;24using namespace lldb_private;25using namespace lldb_private::formatters;26 27std::map<ConstString, CXXFunctionSummaryFormat::Callback> &28NSString_Additionals::GetAdditionalSummaries() {29  static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;30  return g_map;31}32 33bool lldb_private::formatters::NSStringSummaryProvider(34    ValueObject &valobj, Stream &stream,35    const TypeSummaryOptions &summary_options) {36  static constexpr llvm::StringLiteral g_TypeHint("NSString");37 38  ProcessSP process_sp = valobj.GetProcessSP();39  if (!process_sp)40    return false;41 42  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);43 44  if (!runtime)45    return false;46 47  ObjCLanguageRuntime::ClassDescriptorSP descriptor(48      runtime->GetClassDescriptor(valobj));49 50  if (!descriptor.get() || !descriptor->IsValid())51    return false;52 53  uint32_t ptr_size = process_sp->GetAddressByteSize();54 55  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);56 57  if (!valobj_addr)58    return false;59 60  ConstString class_name_cs = descriptor->GetClassName();61  llvm::StringRef class_name = class_name_cs.GetStringRef();62 63  if (class_name.empty())64    return false;65 66  // For tagged pointers, the descriptor has everything needed.67  bool is_tagged = descriptor->GetTaggedPointerInfo();68  if (is_tagged) {69    if (class_name == "NSTaggedPointerString")70      return NSTaggedString_SummaryProvider(valobj, descriptor, stream,71                                            summary_options);72 73    if (class_name == "NSIndirectTaggedPointerString")74      return NSIndirectTaggedString_SummaryProvider(valobj, descriptor, stream,75                                                    summary_options);76  }77 78  auto &additionals_map(NSString_Additionals::GetAdditionalSummaries());79  auto iter = additionals_map.find(class_name_cs), end = additionals_map.end();80  if (iter != end)81    return iter->second(valobj, stream, summary_options);82 83  // if not a tagged pointer that we know about, try the normal route84  uint64_t info_bits_location = valobj_addr + ptr_size;85  if (process_sp->GetByteOrder() != lldb::eByteOrderLittle)86    info_bits_location += 3;87 88  Status error;89 90  uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory(91      info_bits_location, 1, 0, error);92  if (error.Fail())93    return false;94 95  bool is_mutable = (info_bits & 1) == 1;96  bool is_inline = (info_bits & 0x60) == 0;97  bool has_explicit_length = (info_bits & (1 | 4)) != 4;98  bool is_unicode = (info_bits & 0x10) == 0x10;99  bool is_path_store = class_name == "NSPathStore2";100  bool has_null = (info_bits & 8) == 8;101 102  size_t explicit_length = 0;103  if (!has_null && has_explicit_length && !is_path_store) {104    lldb::addr_t explicit_length_offset = 2 * ptr_size;105    if (is_mutable && !is_inline)106      explicit_length_offset =107          explicit_length_offset + ptr_size; //  notInlineMutable.length;108    else if (is_inline)109      explicit_length = explicit_length + 0; // inline1.length;110    else if (!is_inline && !is_mutable)111      explicit_length_offset =112          explicit_length_offset + ptr_size; // notInlineImmutable1.length;113    else114      explicit_length_offset = 0;115 116    if (explicit_length_offset) {117      explicit_length_offset = valobj_addr + explicit_length_offset;118      explicit_length = process_sp->ReadUnsignedIntegerFromMemory(119          explicit_length_offset, 4, 0, error);120    }121  }122 123  const llvm::StringSet<> supported_string_classes = {124      "NSString",     "CFMutableStringRef",125      "CFStringRef",  "__NSCFConstantString",126      "__NSCFString", "NSCFConstantString",127      "NSCFString",   "NSPathStore2"};128  if (supported_string_classes.count(class_name) == 0) {129    // not one of us - but tell me class name130    stream.Printf("class name = %s", class_name_cs.GetCString());131    return true;132  }133 134  llvm::StringRef prefix, suffix;135  if (Language *language = Language::FindPlugin(summary_options.GetLanguage()))136    std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);137 138  StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);139  options.SetPrefixToken(prefix.str());140  options.SetSuffixToken(suffix.str());141 142  if (is_mutable) {143    uint64_t location = 2 * ptr_size + valobj_addr;144    location = process_sp->ReadPointerFromMemory(location, error);145    if (error.Fail())146      return false;147    if (has_explicit_length && is_unicode) {148      options.SetLocation(location);149      options.SetTargetSP(valobj.GetTargetSP());150      options.SetStream(&stream);151      options.SetQuote('"');152      options.SetSourceSize(explicit_length);153      options.SetHasSourceSize(has_explicit_length);154      options.SetNeedsZeroTermination(false);155      options.SetIgnoreMaxLength(summary_options.GetCapping() ==156                                 TypeSummaryCapping::eTypeSummaryUncapped);157      options.SetBinaryZeroIsTerminator(false);158      return StringPrinter::ReadStringAndDumpToStream<159          StringPrinter::StringElementType::UTF16>(options);160    } else {161      options.SetLocation(location + 1);162      options.SetTargetSP(valobj.GetTargetSP());163      options.SetStream(&stream);164      options.SetSourceSize(explicit_length);165      options.SetHasSourceSize(has_explicit_length);166      options.SetNeedsZeroTermination(false);167      options.SetIgnoreMaxLength(summary_options.GetCapping() ==168                                 TypeSummaryCapping::eTypeSummaryUncapped);169      options.SetBinaryZeroIsTerminator(false);170      return StringPrinter::ReadStringAndDumpToStream<171          StringPrinter::StringElementType::ASCII>(options);172    }173  } else if (is_inline && has_explicit_length && !is_unicode &&174             !is_path_store && !is_mutable) {175    uint64_t location = 3 * ptr_size + valobj_addr;176 177    options.SetLocation(location);178    options.SetTargetSP(valobj.GetTargetSP());179    options.SetStream(&stream);180    options.SetQuote('"');181    options.SetSourceSize(explicit_length);182    options.SetHasSourceSize(has_explicit_length);183    options.SetIgnoreMaxLength(summary_options.GetCapping() ==184                               TypeSummaryCapping::eTypeSummaryUncapped);185    return StringPrinter::ReadStringAndDumpToStream<186        StringPrinter::StringElementType::ASCII>(options);187  } else if (is_unicode) {188    uint64_t location = valobj_addr + 2 * ptr_size;189    if (is_inline) {190      if (!has_explicit_length) {191        return false;192      } else193        location += ptr_size;194    } else {195      location = process_sp->ReadPointerFromMemory(location, error);196      if (error.Fail())197        return false;198    }199    options.SetLocation(location);200    options.SetTargetSP(valobj.GetTargetSP());201    options.SetStream(&stream);202    options.SetQuote('"');203    options.SetSourceSize(explicit_length);204    options.SetHasSourceSize(has_explicit_length);205    options.SetNeedsZeroTermination(!has_explicit_length);206    options.SetIgnoreMaxLength(summary_options.GetCapping() ==207                               TypeSummaryCapping::eTypeSummaryUncapped);208    options.SetBinaryZeroIsTerminator(!has_explicit_length);209    return StringPrinter::ReadStringAndDumpToStream<210        StringPrinter::StringElementType::UTF16>(options);211  } else if (is_path_store) {212    // _lengthAndRefCount is the first ivar of NSPathStore2 (after the isa).213    uint64_t length_ivar_offset = 1 * ptr_size;214    CompilerType length_type = valobj.GetCompilerType().GetBasicTypeFromAST(215        lldb::eBasicTypeUnsignedInt);216    ValueObjectSP length_valobj_sp =217        valobj.GetSyntheticChildAtOffset(length_ivar_offset, length_type, true,218                                         ConstString("_lengthAndRefCount"));219    if (!length_valobj_sp)220      return false;221    // Get the length out of _lengthAndRefCount.222    explicit_length = length_valobj_sp->GetValueAsUnsigned(0) >> 20;223    lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4;224 225    options.SetLocation(location);226    options.SetTargetSP(valobj.GetTargetSP());227    options.SetStream(&stream);228    options.SetQuote('"');229    options.SetSourceSize(explicit_length);230    options.SetHasSourceSize(has_explicit_length);231    options.SetNeedsZeroTermination(!has_explicit_length);232    options.SetIgnoreMaxLength(summary_options.GetCapping() ==233                               TypeSummaryCapping::eTypeSummaryUncapped);234    options.SetBinaryZeroIsTerminator(!has_explicit_length);235    return StringPrinter::ReadStringAndDumpToStream<236        StringPrinter::StringElementType::UTF16>(options);237  } else if (is_inline) {238    uint64_t location = valobj_addr + 2 * ptr_size;239    if (!has_explicit_length) {240      // in this kind of string, the byte before the string content is a length241      // byte so let's try and use it to handle the embedded NUL case242      Status error;243      explicit_length =244          process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error);245      has_explicit_length = !(error.Fail() || explicit_length == 0);246      location++;247    }248    options.SetLocation(location);249    options.SetTargetSP(valobj.GetTargetSP());250    options.SetStream(&stream);251    options.SetSourceSize(explicit_length);252    options.SetHasSourceSize(has_explicit_length);253    options.SetNeedsZeroTermination(!has_explicit_length);254    options.SetIgnoreMaxLength(summary_options.GetCapping() ==255                               TypeSummaryCapping::eTypeSummaryUncapped);256    options.SetBinaryZeroIsTerminator(!has_explicit_length);257    if (has_explicit_length)258      return StringPrinter::ReadStringAndDumpToStream<259          StringPrinter::StringElementType::UTF8>(options);260    else261      return StringPrinter::ReadStringAndDumpToStream<262          StringPrinter::StringElementType::ASCII>(options);263  } else {264    uint64_t location = valobj_addr + 2 * ptr_size;265    location = process_sp->ReadPointerFromMemory(location, error);266    if (error.Fail())267      return false;268    if (has_explicit_length && !has_null)269      explicit_length++; // account for the fact that there is no NULL and we270                         // need to have one added271    options.SetLocation(location);272    options.SetTargetSP(valobj.GetTargetSP());273    options.SetStream(&stream);274    options.SetSourceSize(explicit_length);275    options.SetHasSourceSize(has_explicit_length);276    options.SetIgnoreMaxLength(summary_options.GetCapping() ==277                               TypeSummaryCapping::eTypeSummaryUncapped);278    return StringPrinter::ReadStringAndDumpToStream<279        StringPrinter::StringElementType::ASCII>(options);280  }281}282 283bool lldb_private::formatters::NSAttributedStringSummaryProvider(284    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {285  TargetSP target_sp(valobj.GetTargetSP());286  if (!target_sp)287    return false;288  uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize();289  uint64_t pointer_value = valobj.GetValueAsUnsigned(0);290  if (!pointer_value)291    return false;292  pointer_value += addr_size;293  CompilerType type(valobj.GetCompilerType());294  ExecutionContext exe_ctx(target_sp, false);295  ValueObjectSP child_ptr_sp(valobj.CreateValueObjectFromAddress(296      "string_ptr", pointer_value, exe_ctx, type));297  if (!child_ptr_sp)298    return false;299  DataExtractor data;300  Status error;301  child_ptr_sp->GetData(data, error);302  if (error.Fail())303    return false;304  ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData(305      "string_data", data, exe_ctx, type));306  child_sp->GetValueAsUnsigned(0);307  if (child_sp)308    return NSStringSummaryProvider(*child_sp, stream, options);309  return false;310}311 312bool lldb_private::formatters::NSMutableAttributedStringSummaryProvider(313    ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {314  return NSAttributedStringSummaryProvider(valobj, stream, options);315}316 317bool lldb_private::formatters::NSTaggedString_SummaryProvider(318    ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor,319    Stream &stream, const TypeSummaryOptions &summary_options) {320  static constexpr llvm::StringLiteral g_TypeHint("NSString");321 322  if (!descriptor)323    return false;324  uint64_t len_bits = 0, data_bits = 0;325  if (!descriptor->GetTaggedPointerInfo(&len_bits, &data_bits, nullptr))326    return false;327 328  static const int g_MaxNonBitmaskedLen = 7; // TAGGED_STRING_UNPACKED_MAXLEN329  static const int g_SixbitMaxLen = 9;330  static const int g_fiveBitMaxLen = 11;331 332  static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013"333                                          "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX";334 335  if (len_bits > g_fiveBitMaxLen)336    return false;337 338  llvm::StringRef prefix, suffix;339  if (Language *language = Language::FindPlugin(summary_options.GetLanguage()))340    std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);341 342  // this is a fairly ugly trick - pretend that the numeric value is actually a343  // char* this works under a few assumptions: little endian architecture344  // sizeof(uint64_t) > g_MaxNonBitmaskedLen345  if (len_bits <= g_MaxNonBitmaskedLen) {346    stream << prefix;347    stream.Printf("\"%s\"", (const char *)&data_bits);348    stream << suffix;349    return true;350  }351 352  // if the data is bitmasked, we need to actually process the bytes353  uint8_t bitmask = 0;354  uint8_t shift_offset = 0;355 356  if (len_bits <= g_SixbitMaxLen) {357    bitmask = 0x03f;358    shift_offset = 6;359  } else {360    bitmask = 0x01f;361    shift_offset = 5;362  }363 364  std::vector<uint8_t> bytes;365  bytes.resize(len_bits);366  for (; len_bits > 0; data_bits >>= shift_offset, --len_bits) {367    uint8_t packed = data_bits & bitmask;368    bytes.insert(bytes.begin(), sixBitToCharLookup[packed]);369  }370 371  stream << prefix;372  stream.Printf("\"%s\"", &bytes[0]);373  stream << suffix;374  return true;375}376 377bool lldb_private::formatters::NSIndirectTaggedString_SummaryProvider(378    ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor,379    Stream &stream, const TypeSummaryOptions &summary_options) {380  if (!descriptor)381    return false;382 383  uint64_t payload = 0;384  if (!descriptor->GetTaggedPointerInfo(nullptr, nullptr, &payload))385    return false;386 387  // First 47 bits are the address of the contents.388  addr_t ptr = payload & 0x7fffffffffffULL;389  // Next 13 bits are the string's length.390  size_t size = (payload >> 47) & 0x1fff;391 392  Status status;393  std::vector<char> buf(size);394  if (auto process_sp = valobj.GetProcessSP())395    if (process_sp->ReadMemory(ptr, buf.data(), size, status)) {396      llvm::StringRef prefix, suffix;397      if (auto *language = Language::FindPlugin(summary_options.GetLanguage()))398        std::tie(prefix, suffix) =399            language->GetFormatterPrefixSuffix("NSString");400      stream << prefix << '"';401      stream.PutCString({buf.data(), size});402      stream << '"' << suffix;403      return true;404    }405 406  if (status.Fail())407    stream.Format("<{0}>", status);408  return false;409}410