Commit 9d5e95d0 authored by Jonas Devlieghere's avatar Jonas Devlieghere
Browse files

Re-land "[lldb] Upstream support for Foundation constant classes"

Upstream support for NSConstantArray, NSConstantIntegerNumber,
NSConstant{Float,Double}Number and NSConstantDictionary.

We would've upstreamed this earlier but testing it requires
-fno-constant-nsnumber-literals, -fno-constant-nsarray-literals and
-fno-constant-nsdictionary-literals which haven't been upstreamed yet.
As a temporary workaround use the system compiler (xcrun clang) for the
constant variant of the tests.

I'm just upstreaming this. The patch and the tests were all authored by
Fred Riss.

Differential revision: https://reviews.llvm.org/D107660
parent 4e5af6ef
Loading
Loading
Loading
Loading
+99 −3
Original line number Diff line number Diff line
@@ -7,7 +7,9 @@
//===----------------------------------------------------------------------===//

#include "Cocoa.h"
#include "NSString.h"

#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
#include "lldb/Core/Mangled.h"
#include "lldb/Core/ValueObject.h"
@@ -28,9 +30,37 @@
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/bit.h"

#include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.h"

#include "NSString.h"
// Objective-C Type Encoding
#define _C_ID       '@'
#define _C_CLASS    '#'
#define _C_SEL      ':'
#define _C_CHR      'c'
#define _C_UCHR     'C'
#define _C_SHT      's'
#define _C_USHT     'S'
#define _C_INT      'i'
#define _C_UINT     'I'
#define _C_LNG      'l'
#define _C_ULNG     'L'
#define _C_LNG_LNG  'q'
#define _C_ULNG_LNG 'Q'
#define _C_FLT      'f'
#define _C_DBL      'd'
#define _C_BFLD     'b'
#define _C_BOOL     'B'
#define _C_VOID     'v'
#define _C_UNDEF    '?'
#define _C_PTR      '^'
#define _C_CHARPTR  '*'
#define _C_ATOM     '%'
#define _C_ARY_B    '['
#define _C_ARY_E    ']'
#define _C_UNION_B  '('
#define _C_UNION_E  ')'
#define _C_STRUCT_B '{'
#define _C_STRUCT_E '}'
#define _C_VECTOR   '!'
#define _C_CONST    'r'

using namespace lldb;
using namespace lldb_private;
@@ -456,6 +486,72 @@ bool lldb_private::formatters::NSNumberSummaryProvider(
  if (class_name == "NSDecimalNumber")
    return NSDecimalNumberSummaryProvider(valobj, stream, options);

  if (class_name == "NSConstantIntegerNumber") {
    Status error;
    int64_t value = process_sp->ReadSignedIntegerFromMemory(
        valobj_addr + 2 * ptr_size, 8, 0, error);
    if (error.Fail())
      return false;
    uint64_t encoding_addr = process_sp->ReadUnsignedIntegerFromMemory(
        valobj_addr + ptr_size, ptr_size, 0, error);
    if (error.Fail())
      return false;
    char encoding =
        process_sp->ReadUnsignedIntegerFromMemory(encoding_addr, 1, 0, error);
    if (error.Fail())
      return false;

    switch (encoding) {
    case _C_CHR:
      NSNumber_FormatChar(valobj, stream, (char)value, options.GetLanguage());
      return true;
    case _C_SHT:
      NSNumber_FormatShort(valobj, stream, (short)value, options.GetLanguage());
      return true;
    case _C_INT:
      NSNumber_FormatInt(valobj, stream, (int)value, options.GetLanguage());
      return true;
    case _C_LNG:
    case _C_LNG_LNG:
      NSNumber_FormatLong(valobj, stream, value, options.GetLanguage());
      return true;

    case _C_UCHR:
    case _C_USHT:
    case _C_UINT:
    case _C_ULNG:
    case _C_ULNG_LNG:
      stream.Printf("%" PRIu64, value);
      return true;
    }

    return false;
  }

  if (class_name == "NSConstantFloatNumber") {
    Status error;
    uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(
        valobj_addr + ptr_size, 4, 0, error);
    if (error.Fail())
      return false;
    float flt_value = 0.0f;
    memcpy(&flt_value, &flt_as_int, sizeof(flt_as_int));
    NSNumber_FormatFloat(valobj, stream, flt_value, options.GetLanguage());
    return true;
  }

  if (class_name == "NSConstantDoubleNumber") {
    Status error;
    uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(
        valobj_addr + ptr_size, 8, 0, error);
    if (error.Fail())
      return false;
    double dbl_value = 0.0;
    memcpy(&dbl_value, &dbl_as_lng, sizeof(dbl_as_lng));
    NSNumber_FormatDouble(valobj, stream, dbl_value, options.GetLanguage());
    return true;
  }

  if (class_name == "NSNumber" || class_name == "__NSCFNumber") {
    int64_t value = 0;
    uint64_t i_bits = 0;
+26 −0
Original line number Diff line number Diff line
@@ -280,6 +280,22 @@ namespace Foundation1436 {
    }
}

namespace ConstantArray {

struct ConstantArray32 {
  uint64_t used;
  uint32_t list;
};

struct ConstantArray64 {
  uint64_t used;
  uint64_t list;
};

using NSConstantArraySyntheticFrontEnd =
    GenericNSArrayISyntheticFrontEnd<ConstantArray32, ConstantArray64, false>;
} // namespace ConstantArray

class NSArray0SyntheticFrontEnd : public SyntheticChildrenFrontEnd {
public:
  NSArray0SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
@@ -356,6 +372,7 @@ bool lldb_private::formatters::NSArraySummaryProvider(
  static const ConstString g_NSArrayMLegacy("__NSArrayM_Legacy");
  static const ConstString g_NSArrayMImmutable("__NSArrayM_Immutable");
  static const ConstString g_NSCallStackArray("_NSCallStackArray");
  static const ConstString g_NSConstantArray("NSConstantArray");

  if (class_name.IsEmpty())
    return false;
@@ -366,6 +383,12 @@ bool lldb_private::formatters::NSArraySummaryProvider(
                                                      ptr_size, 0, error);
    if (error.Fail())
      return false;
  } else if (class_name == g_NSConstantArray) {
    Status error;
    value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 8,
                                                      0, error);
    if (error.Fail())
      return false;
  } else if (class_name == g_NSArrayM) {
    AppleObjCRuntime *apple_runtime =
    llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
@@ -803,6 +826,7 @@ lldb_private::formatters::NSArraySyntheticFrontEndCreator(
  ConstString class_name(descriptor->GetClassName());

  static const ConstString g_NSArrayI("__NSArrayI");
  static const ConstString g_NSConstantArray("NSConstantArray");
  static const ConstString g_NSArrayI_Transfer("__NSArrayI_Transfer");
  static const ConstString g_NSFrozenArrayM("__NSFrozenArrayM");
  static const ConstString g_NSArrayM("__NSArrayM");
@@ -823,6 +847,8 @@ lldb_private::formatters::NSArraySyntheticFrontEndCreator(
    return (new Foundation1300::NSArrayISyntheticFrontEnd(valobj_sp));
  } else if (class_name == g_NSArrayI_Transfer) {
      return (new Foundation1436::NSArrayI_TransferSyntheticFrontEnd(valobj_sp));
  } else if (class_name == g_NSConstantArray) {
    return new ConstantArray::NSConstantArraySyntheticFrontEnd(valobj_sp);
  } else if (class_name == g_NSFrozenArrayM) {
    return (new Foundation1436::NSFrozenArrayMSyntheticFrontEnd(valobj_sp));
  } else if (class_name == g_NSArray0) {
+161 −7
Original line number Diff line number Diff line
@@ -142,6 +142,38 @@ private:
  std::vector<DictionaryItemDescriptor> m_children;
};

class NSConstantDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
public:
  NSConstantDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);

  size_t CalculateNumChildren() override;

  lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;

  bool Update() override;

  bool MightHaveChildren() override;

  size_t GetIndexOfChildWithName(ConstString name) override;

private:
  ExecutionContextRef m_exe_ctx_ref;
  CompilerType m_pair_type;
  uint8_t m_ptr_size = 8;
  lldb::ByteOrder m_order = lldb::eByteOrderInvalid;
  unsigned int m_size = 0;
  lldb::addr_t m_keys_ptr = LLDB_INVALID_ADDRESS;
  lldb::addr_t m_objects_ptr = LLDB_INVALID_ADDRESS;

  struct DictionaryItemDescriptor {
    lldb::addr_t key_ptr;
    lldb::addr_t val_ptr;
    lldb::ValueObjectSP valobj_sp;
  };

  std::vector<DictionaryItemDescriptor> m_children;
};

class NSCFDictionarySyntheticFrontEnd : public SyntheticChildrenFrontEnd {
public:
  NSCFDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp);
@@ -416,6 +448,7 @@ bool lldb_private::formatters::NSDictionarySummaryProvider(
  static const ConstString g_DictionaryCF("__CFDictionary");
  static const ConstString g_DictionaryNSCF("__NSCFDictionary");
  static const ConstString g_DictionaryCFRef("CFDictionaryRef");
  static const ConstString g_ConstantDictionary("NSConstantDictionary");

  if (class_name.IsEmpty())
    return false;
@@ -428,8 +461,14 @@ bool lldb_private::formatters::NSDictionarySummaryProvider(
      return false;

    value &= (is_64bit ? ~0xFC00000000000000UL : ~0xFC000000U);
  } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy 
             || class_name == g_DictionaryMFrozen) {
  } else if (class_name == g_ConstantDictionary) {
    Status error;
    value = process_sp->ReadUnsignedIntegerFromMemory(
        valobj_addr + 2 * ptr_size, ptr_size, 0, error);
    if (error.Fail())
      return false;
  } else if (class_name == g_DictionaryM || class_name == g_DictionaryMLegacy ||
             class_name == g_DictionaryMFrozen) {
    AppleObjCRuntime *apple_runtime =
    llvm::dyn_cast_or_null<AppleObjCRuntime>(runtime);
    Status error;
@@ -447,8 +486,7 @@ bool lldb_private::formatters::NSDictionarySummaryProvider(
    value = 1;
  } else if (class_name == g_Dictionary0) {
    value = 0;
  } else if (class_name == g_DictionaryCF ||
             class_name == g_DictionaryNSCF ||
  } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF ||
             class_name == g_DictionaryCFRef) {
    ExecutionContext exe_ctx(process_sp);
    CFBasicHash cfbh;
@@ -517,12 +555,15 @@ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator(
  static const ConstString g_DictionaryCF("__CFDictionary");
  static const ConstString g_DictionaryNSCF("__NSCFDictionary");
  static const ConstString g_DictionaryCFRef("CFDictionaryRef");
  static const ConstString g_ConstantDictionary("NSConstantDictionary");

  if (class_name.IsEmpty())
    return nullptr;

  if (class_name == g_DictionaryI) {
    return (new NSDictionaryISyntheticFrontEnd(valobj_sp));
  } else if (class_name == g_ConstantDictionary) {
    return (new NSConstantDictionarySyntheticFrontEnd(valobj_sp));
  } else if (class_name == g_DictionaryM || class_name == g_DictionaryMFrozen) {
    if (runtime->GetFoundationVersion() >= 1437) {
      return (new Foundation1437::NSDictionaryMSyntheticFrontEnd(valobj_sp));
@@ -535,8 +576,7 @@ lldb_private::formatters::NSDictionarySyntheticFrontEndCreator(
    return (new Foundation1100::NSDictionaryMSyntheticFrontEnd(valobj_sp));
  } else if (class_name == g_Dictionary1) {
    return (new NSDictionary1SyntheticFrontEnd(valobj_sp));
  } else if (class_name == g_DictionaryCF ||
             class_name == g_DictionaryNSCF ||
  } else if (class_name == g_DictionaryCF || class_name == g_DictionaryNSCF ||
             class_name == g_DictionaryCFRef) {
    return (new NSCFDictionarySyntheticFrontEnd(valobj_sp));
  } else {
@@ -830,6 +870,120 @@ lldb_private::formatters::NSCFDictionarySyntheticFrontEnd::GetChildAtIndex(
  return dict_item.valobj_sp;
}

lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
    NSConstantDictionarySyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
    : SyntheticChildrenFrontEnd(*valobj_sp) {}

size_t lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
    GetIndexOfChildWithName(ConstString name) {
  const char *item_name = name.GetCString();
  uint32_t idx = ExtractIndexFromString(item_name);
  if (idx < UINT32_MAX && idx >= CalculateNumChildren())
    return UINT32_MAX;
  return idx;
}

size_t lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
    CalculateNumChildren() {
  return m_size;
}

bool lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::Update() {
  ValueObjectSP valobj_sp = m_backend.GetSP();
  if (!valobj_sp)
    return false;
  m_exe_ctx_ref = valobj_sp->GetExecutionContextRef();
  Status error;
  error.Clear();
  lldb::ProcessSP process_sp(valobj_sp->GetProcessSP());
  if (!process_sp)
    return false;
  m_ptr_size = process_sp->GetAddressByteSize();
  m_order = process_sp->GetByteOrder();
  uint64_t valobj_addr = valobj_sp->GetValueAsUnsigned(0);
  m_size = process_sp->ReadUnsignedIntegerFromMemory(
      valobj_addr + 2 * m_ptr_size, m_ptr_size, 0, error);
  if (error.Fail())
    return false;
  m_keys_ptr =
      process_sp->ReadPointerFromMemory(valobj_addr + 3 * m_ptr_size, error);
  if (error.Fail())
    return false;
  m_objects_ptr =
      process_sp->ReadPointerFromMemory(valobj_addr + 4 * m_ptr_size, error);
  return !error.Fail();
}

bool lldb_private::formatters::NSConstantDictionarySyntheticFrontEnd::
    MightHaveChildren() {
  return true;
}

lldb::ValueObjectSP lldb_private::formatters::
    NSConstantDictionarySyntheticFrontEnd::GetChildAtIndex(size_t idx) {
  uint32_t num_children = CalculateNumChildren();

  if (idx >= num_children)
    return lldb::ValueObjectSP();

  if (m_children.empty()) {
    // do the scan phase
    lldb::addr_t key_at_idx = 0, val_at_idx = 0;
    ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP();
    if (!process_sp)
      return lldb::ValueObjectSP();

    for (unsigned int child = 0; child < num_children; ++child) {
      Status error;
      key_at_idx = process_sp->ReadPointerFromMemory(
          m_keys_ptr + child * m_ptr_size, error);
      if (error.Fail())
        return lldb::ValueObjectSP();
      val_at_idx = process_sp->ReadPointerFromMemory(
          m_objects_ptr + child * m_ptr_size, error);
      if (error.Fail())
        return lldb::ValueObjectSP();
      DictionaryItemDescriptor descriptor = {key_at_idx, val_at_idx,
                                             lldb::ValueObjectSP()};
      m_children.push_back(descriptor);
    }
  }

  if (idx >= m_children.size()) // should never happen
    return lldb::ValueObjectSP();

  DictionaryItemDescriptor &dict_item = m_children[idx];
  if (!dict_item.valobj_sp) {
    if (!m_pair_type.IsValid()) {
      TargetSP target_sp(m_backend.GetTargetSP());
      if (!target_sp)
        return ValueObjectSP();
      m_pair_type = GetLLDBNSPairType(target_sp);
    }
    if (!m_pair_type.IsValid())
      return ValueObjectSP();

    DataBufferSP buffer_sp(new DataBufferHeap(2 * m_ptr_size, 0));

    if (m_ptr_size == 8) {
      uint64_t *data_ptr = (uint64_t *)buffer_sp->GetBytes();
      *data_ptr = dict_item.key_ptr;
      *(data_ptr + 1) = dict_item.val_ptr;
    } else {
      uint32_t *data_ptr = (uint32_t *)buffer_sp->GetBytes();
      *data_ptr = dict_item.key_ptr;
      *(data_ptr + 1) = dict_item.val_ptr;
    }

    StreamString idx_name;
    idx_name.Printf("[%" PRIu64 "]", (uint64_t)idx);
    DataExtractor data(buffer_sp, m_order, m_ptr_size);
    dict_item.valobj_sp = CreateValueObjectFromData(idx_name.GetString(), data,
                                                    m_exe_ctx_ref, m_pair_type);
  }
  return dict_item.valobj_sp;
}

lldb_private::formatters::NSDictionary1SyntheticFrontEnd::
    NSDictionary1SyntheticFrontEnd(lldb::ValueObjectSP valobj_sp)
    : SyntheticChildrenFrontEnd(*valobj_sp.get()), m_pair(nullptr) {}
+28 −0
Original line number Diff line number Diff line
@@ -403,6 +403,9 @@ static void LoadObjCFormatters(TypeCategoryImplSP objc_category_sp) {
  AddCXXSummary(
      objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
      "NSArray summary provider", ConstString("NSArray"), appkit_flags);
  AddCXXSummary(
      objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
      "NSArray summary provider", ConstString("NSConstantArray"), appkit_flags);
  AddCXXSummary(
      objc_category_sp, lldb_private::formatters::NSArraySummaryProvider,
      "NSArray summary provider", ConstString("NSMutableArray"), appkit_flags);
@@ -437,6 +440,10 @@ static void LoadObjCFormatters(TypeCategoryImplSP objc_category_sp) {
                lldb_private::formatters::NSDictionarySummaryProvider<false>,
                "NSDictionary summary provider", ConstString("NSDictionary"),
                appkit_flags);
  AddCXXSummary(objc_category_sp,
                lldb_private::formatters::NSDictionarySummaryProvider<false>,
                "NSDictionary summary provider",
                ConstString("NSConstantDictionary"), appkit_flags);
  AddCXXSummary(objc_category_sp,
                lldb_private::formatters::NSDictionarySummaryProvider<false>,
                "NSDictionary summary provider",
@@ -543,6 +550,10 @@ static void LoadObjCFormatters(TypeCategoryImplSP objc_category_sp) {
                  lldb_private::formatters::NSArraySyntheticFrontEndCreator,
                  "NSArray synthetic children", ConstString("NSArray"),
                  ScriptedSyntheticChildren::Flags());
  AddCXXSynthetic(objc_category_sp,
                  lldb_private::formatters::NSArraySyntheticFrontEndCreator,
                  "NSArray synthetic children", ConstString("NSConstantArray"),
                  ScriptedSyntheticChildren::Flags());
  AddCXXSynthetic(objc_category_sp,
                  lldb_private::formatters::NSArraySyntheticFrontEndCreator,
                  "NSArray synthetic children", ConstString("NSMutableArray"),
@@ -570,6 +581,11 @@ static void LoadObjCFormatters(TypeCategoryImplSP objc_category_sp) {
      lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
      "NSDictionary synthetic children", ConstString("__NSDictionaryM"),
      ScriptedSyntheticChildren::Flags());
  AddCXXSynthetic(
      objc_category_sp,
      lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
      "NSDictionary synthetic children", ConstString("NSConstantDictionary"),
      ScriptedSyntheticChildren::Flags());
  AddCXXSynthetic(
      objc_category_sp,
      lldb_private::formatters::NSDictionarySyntheticFrontEndCreator,
@@ -791,6 +807,18 @@ static void LoadObjCFormatters(TypeCategoryImplSP objc_category_sp) {
  AddCXXSummary(
      objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider,
      "NSNumber summary provider", ConstString("NSNumber"), appkit_flags);
  AddCXXSummary(objc_category_sp,
                lldb_private::formatters::NSNumberSummaryProvider,
                "NSNumber summary provider",
                ConstString("NSConstantIntegerNumber"), appkit_flags);
  AddCXXSummary(objc_category_sp,
                lldb_private::formatters::NSNumberSummaryProvider,
                "NSNumber summary provider",
                ConstString("NSConstantDoubleNumber"), appkit_flags);
  AddCXXSummary(objc_category_sp,
                lldb_private::formatters::NSNumberSummaryProvider,
                "NSNumber summary provider",
                ConstString("NSConstantFloatNumber"), appkit_flags);
  AddCXXSummary(
      objc_category_sp, lldb_private::formatters::NSNumberSummaryProvider,
      "CFNumberRef summary provider", ConstString("CFNumberRef"), appkit_flags);
+13 −2
Original line number Diff line number Diff line
@@ -14,8 +14,19 @@ class ObjCDataFormatterTestCase(TestBase):

   mydir = TestBase.compute_mydir(__file__)

   def appkit_tester_impl(self, commands):
   def appkit_tester_impl(self, commands, use_constant_classes):
      if use_constant_classes:
         self.build()
      else:
         disable_constant_classes = {
            'CC':
            'xcrun clang', # FIXME: Remove when flags are available upstream.
            'CFLAGS_EXTRAS':
            '-fno-constant-nsnumber-literals ' +
            '-fno-constant-nsarray-literals ' +
            '-fno-constant-nsdictionary-literals'
         }
         self.build(dictionary=disable_constant_classes)
      self.appkit_common_data_formatters_command()
      commands()

Loading