Path: blob/main/contrib/llvm-project/lldb/source/Plugins/Language/ObjC/NSString.cpp
39644 views
//===-- NSString.cpp ------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "NSString.h"910#include "lldb/Core/ValueObject.h"11#include "lldb/Core/ValueObjectConstResult.h"12#include "lldb/DataFormatters/FormattersHelpers.h"13#include "lldb/DataFormatters/StringPrinter.h"14#include "lldb/Target/Language.h"15#include "lldb/Target/Target.h"16#include "lldb/Utility/ConstString.h"17#include "lldb/Utility/DataBufferHeap.h"18#include "lldb/Utility/Endian.h"19#include "lldb/Utility/Status.h"20#include "lldb/Utility/Stream.h"2122using namespace lldb;23using namespace lldb_private;24using namespace lldb_private::formatters;2526std::map<ConstString, CXXFunctionSummaryFormat::Callback> &27NSString_Additionals::GetAdditionalSummaries() {28static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;29return g_map;30}3132bool lldb_private::formatters::NSStringSummaryProvider(33ValueObject &valobj, Stream &stream,34const TypeSummaryOptions &summary_options) {35static constexpr llvm::StringLiteral g_TypeHint("NSString");3637ProcessSP process_sp = valobj.GetProcessSP();38if (!process_sp)39return false;4041ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);4243if (!runtime)44return false;4546ObjCLanguageRuntime::ClassDescriptorSP descriptor(47runtime->GetClassDescriptor(valobj));4849if (!descriptor.get() || !descriptor->IsValid())50return false;5152uint32_t ptr_size = process_sp->GetAddressByteSize();5354lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);5556if (!valobj_addr)57return false;5859ConstString class_name_cs = descriptor->GetClassName();60llvm::StringRef class_name = class_name_cs.GetStringRef();6162if (class_name.empty())63return false;6465bool is_tagged_ptr = class_name == "NSTaggedPointerString" &&66descriptor->GetTaggedPointerInfo();67// for a tagged pointer, the descriptor has everything we need68if (is_tagged_ptr)69return NSTaggedString_SummaryProvider(valobj, descriptor, stream,70summary_options);7172auto &additionals_map(NSString_Additionals::GetAdditionalSummaries());73auto iter = additionals_map.find(class_name_cs), end = additionals_map.end();74if (iter != end)75return iter->second(valobj, stream, summary_options);7677// if not a tagged pointer that we know about, try the normal route78uint64_t info_bits_location = valobj_addr + ptr_size;79if (process_sp->GetByteOrder() != lldb::eByteOrderLittle)80info_bits_location += 3;8182Status error;8384uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory(85info_bits_location, 1, 0, error);86if (error.Fail())87return false;8889bool is_mutable = (info_bits & 1) == 1;90bool is_inline = (info_bits & 0x60) == 0;91bool has_explicit_length = (info_bits & (1 | 4)) != 4;92bool is_unicode = (info_bits & 0x10) == 0x10;93bool is_path_store = class_name == "NSPathStore2";94bool has_null = (info_bits & 8) == 8;9596size_t explicit_length = 0;97if (!has_null && has_explicit_length && !is_path_store) {98lldb::addr_t explicit_length_offset = 2 * ptr_size;99if (is_mutable && !is_inline)100explicit_length_offset =101explicit_length_offset + ptr_size; // notInlineMutable.length;102else if (is_inline)103explicit_length = explicit_length + 0; // inline1.length;104else if (!is_inline && !is_mutable)105explicit_length_offset =106explicit_length_offset + ptr_size; // notInlineImmutable1.length;107else108explicit_length_offset = 0;109110if (explicit_length_offset) {111explicit_length_offset = valobj_addr + explicit_length_offset;112explicit_length = process_sp->ReadUnsignedIntegerFromMemory(113explicit_length_offset, 4, 0, error);114}115}116117const llvm::StringSet<> supported_string_classes = {118"NSString", "CFMutableStringRef",119"CFStringRef", "__NSCFConstantString",120"__NSCFString", "NSCFConstantString",121"NSCFString", "NSPathStore2"};122if (supported_string_classes.count(class_name) == 0) {123// not one of us - but tell me class name124stream.Printf("class name = %s", class_name_cs.GetCString());125return true;126}127128llvm::StringRef prefix, suffix;129if (Language *language = Language::FindPlugin(summary_options.GetLanguage()))130std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);131132StringPrinter::ReadStringAndDumpToStreamOptions options(valobj);133options.SetPrefixToken(prefix.str());134options.SetSuffixToken(suffix.str());135136if (is_mutable) {137uint64_t location = 2 * ptr_size + valobj_addr;138location = process_sp->ReadPointerFromMemory(location, error);139if (error.Fail())140return false;141if (has_explicit_length && is_unicode) {142options.SetLocation(location);143options.SetTargetSP(valobj.GetTargetSP());144options.SetStream(&stream);145options.SetQuote('"');146options.SetSourceSize(explicit_length);147options.SetHasSourceSize(has_explicit_length);148options.SetNeedsZeroTermination(false);149options.SetIgnoreMaxLength(summary_options.GetCapping() ==150TypeSummaryCapping::eTypeSummaryUncapped);151options.SetBinaryZeroIsTerminator(false);152return StringPrinter::ReadStringAndDumpToStream<153StringPrinter::StringElementType::UTF16>(options);154} else {155options.SetLocation(location + 1);156options.SetTargetSP(valobj.GetTargetSP());157options.SetStream(&stream);158options.SetSourceSize(explicit_length);159options.SetHasSourceSize(has_explicit_length);160options.SetNeedsZeroTermination(false);161options.SetIgnoreMaxLength(summary_options.GetCapping() ==162TypeSummaryCapping::eTypeSummaryUncapped);163options.SetBinaryZeroIsTerminator(false);164return StringPrinter::ReadStringAndDumpToStream<165StringPrinter::StringElementType::ASCII>(options);166}167} else if (is_inline && has_explicit_length && !is_unicode &&168!is_path_store && !is_mutable) {169uint64_t location = 3 * ptr_size + valobj_addr;170171options.SetLocation(location);172options.SetTargetSP(valobj.GetTargetSP());173options.SetStream(&stream);174options.SetQuote('"');175options.SetSourceSize(explicit_length);176options.SetHasSourceSize(has_explicit_length);177options.SetIgnoreMaxLength(summary_options.GetCapping() ==178TypeSummaryCapping::eTypeSummaryUncapped);179return StringPrinter::ReadStringAndDumpToStream<180StringPrinter::StringElementType::ASCII>(options);181} else if (is_unicode) {182uint64_t location = valobj_addr + 2 * ptr_size;183if (is_inline) {184if (!has_explicit_length) {185return false;186} else187location += ptr_size;188} else {189location = process_sp->ReadPointerFromMemory(location, error);190if (error.Fail())191return false;192}193options.SetLocation(location);194options.SetTargetSP(valobj.GetTargetSP());195options.SetStream(&stream);196options.SetQuote('"');197options.SetSourceSize(explicit_length);198options.SetHasSourceSize(has_explicit_length);199options.SetNeedsZeroTermination(!has_explicit_length);200options.SetIgnoreMaxLength(summary_options.GetCapping() ==201TypeSummaryCapping::eTypeSummaryUncapped);202options.SetBinaryZeroIsTerminator(!has_explicit_length);203return StringPrinter::ReadStringAndDumpToStream<204StringPrinter::StringElementType::UTF16>(options);205} else if (is_path_store) {206// _lengthAndRefCount is the first ivar of NSPathStore2 (after the isa).207uint64_t length_ivar_offset = 1 * ptr_size;208CompilerType length_type = valobj.GetCompilerType().GetBasicTypeFromAST(209lldb::eBasicTypeUnsignedInt);210ValueObjectSP length_valobj_sp =211valobj.GetSyntheticChildAtOffset(length_ivar_offset, length_type, true,212ConstString("_lengthAndRefCount"));213if (!length_valobj_sp)214return false;215// Get the length out of _lengthAndRefCount.216explicit_length = length_valobj_sp->GetValueAsUnsigned(0) >> 20;217lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4;218219options.SetLocation(location);220options.SetTargetSP(valobj.GetTargetSP());221options.SetStream(&stream);222options.SetQuote('"');223options.SetSourceSize(explicit_length);224options.SetHasSourceSize(has_explicit_length);225options.SetNeedsZeroTermination(!has_explicit_length);226options.SetIgnoreMaxLength(summary_options.GetCapping() ==227TypeSummaryCapping::eTypeSummaryUncapped);228options.SetBinaryZeroIsTerminator(!has_explicit_length);229return StringPrinter::ReadStringAndDumpToStream<230StringPrinter::StringElementType::UTF16>(options);231} else if (is_inline) {232uint64_t location = valobj_addr + 2 * ptr_size;233if (!has_explicit_length) {234// in this kind of string, the byte before the string content is a length235// byte so let's try and use it to handle the embedded NUL case236Status error;237explicit_length =238process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error);239has_explicit_length = !(error.Fail() || explicit_length == 0);240location++;241}242options.SetLocation(location);243options.SetTargetSP(valobj.GetTargetSP());244options.SetStream(&stream);245options.SetSourceSize(explicit_length);246options.SetHasSourceSize(has_explicit_length);247options.SetNeedsZeroTermination(!has_explicit_length);248options.SetIgnoreMaxLength(summary_options.GetCapping() ==249TypeSummaryCapping::eTypeSummaryUncapped);250options.SetBinaryZeroIsTerminator(!has_explicit_length);251if (has_explicit_length)252return StringPrinter::ReadStringAndDumpToStream<253StringPrinter::StringElementType::UTF8>(options);254else255return StringPrinter::ReadStringAndDumpToStream<256StringPrinter::StringElementType::ASCII>(options);257} else {258uint64_t location = valobj_addr + 2 * ptr_size;259location = process_sp->ReadPointerFromMemory(location, error);260if (error.Fail())261return false;262if (has_explicit_length && !has_null)263explicit_length++; // account for the fact that there is no NULL and we264// need to have one added265options.SetLocation(location);266options.SetTargetSP(valobj.GetTargetSP());267options.SetStream(&stream);268options.SetSourceSize(explicit_length);269options.SetHasSourceSize(has_explicit_length);270options.SetIgnoreMaxLength(summary_options.GetCapping() ==271TypeSummaryCapping::eTypeSummaryUncapped);272return StringPrinter::ReadStringAndDumpToStream<273StringPrinter::StringElementType::ASCII>(options);274}275}276277bool lldb_private::formatters::NSAttributedStringSummaryProvider(278ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {279TargetSP target_sp(valobj.GetTargetSP());280if (!target_sp)281return false;282uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize();283uint64_t pointer_value = valobj.GetValueAsUnsigned(0);284if (!pointer_value)285return false;286pointer_value += addr_size;287CompilerType type(valobj.GetCompilerType());288ExecutionContext exe_ctx(target_sp, false);289ValueObjectSP child_ptr_sp(valobj.CreateValueObjectFromAddress(290"string_ptr", pointer_value, exe_ctx, type));291if (!child_ptr_sp)292return false;293DataExtractor data;294Status error;295child_ptr_sp->GetData(data, error);296if (error.Fail())297return false;298ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData(299"string_data", data, exe_ctx, type));300child_sp->GetValueAsUnsigned(0);301if (child_sp)302return NSStringSummaryProvider(*child_sp, stream, options);303return false;304}305306bool lldb_private::formatters::NSMutableAttributedStringSummaryProvider(307ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {308return NSAttributedStringSummaryProvider(valobj, stream, options);309}310311bool lldb_private::formatters::NSTaggedString_SummaryProvider(312ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor,313Stream &stream, const TypeSummaryOptions &summary_options) {314static constexpr llvm::StringLiteral g_TypeHint("NSString");315316if (!descriptor)317return false;318uint64_t len_bits = 0, data_bits = 0;319if (!descriptor->GetTaggedPointerInfo(&len_bits, &data_bits, nullptr))320return false;321322static const int g_MaxNonBitmaskedLen = 7; // TAGGED_STRING_UNPACKED_MAXLEN323static const int g_SixbitMaxLen = 9;324static const int g_fiveBitMaxLen = 11;325326static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013"327"bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX";328329if (len_bits > g_fiveBitMaxLen)330return false;331332llvm::StringRef prefix, suffix;333if (Language *language = Language::FindPlugin(summary_options.GetLanguage()))334std::tie(prefix, suffix) = language->GetFormatterPrefixSuffix(g_TypeHint);335336// this is a fairly ugly trick - pretend that the numeric value is actually a337// char* this works under a few assumptions: little endian architecture338// sizeof(uint64_t) > g_MaxNonBitmaskedLen339if (len_bits <= g_MaxNonBitmaskedLen) {340stream << prefix;341stream.Printf("\"%s\"", (const char *)&data_bits);342stream << suffix;343return true;344}345346// if the data is bitmasked, we need to actually process the bytes347uint8_t bitmask = 0;348uint8_t shift_offset = 0;349350if (len_bits <= g_SixbitMaxLen) {351bitmask = 0x03f;352shift_offset = 6;353} else {354bitmask = 0x01f;355shift_offset = 5;356}357358std::vector<uint8_t> bytes;359bytes.resize(len_bits);360for (; len_bits > 0; data_bits >>= shift_offset, --len_bits) {361uint8_t packed = data_bits & bitmask;362bytes.insert(bytes.begin(), sixBitToCharLookup[packed]);363}364365stream << prefix;366stream.Printf("\"%s\"", &bytes[0]);367stream << suffix;368return true;369}370371372