1351290Sdim//===-- ObjCLanguageRuntime.h -----------------------------------*- C++ -*-===//
2351290Sdim//
3351290Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4351290Sdim// See https://llvm.org/LICENSE.txt for license information.
5351290Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6351290Sdim//
7351290Sdim//===----------------------------------------------------------------------===//
8351290Sdim
9351290Sdim#ifndef liblldb_ObjCLanguageRuntime_h_
10351290Sdim#define liblldb_ObjCLanguageRuntime_h_
11351290Sdim
12351290Sdim#include <functional>
13351290Sdim#include <map>
14351290Sdim#include <memory>
15351290Sdim#include <unordered_set>
16351290Sdim
17351290Sdim#include "llvm/Support/Casting.h"
18351290Sdim
19351290Sdim#include "lldb/Breakpoint/BreakpointPrecondition.h"
20360784Sdim#include "lldb/Core/ClangForward.h"
21351290Sdim#include "lldb/Core/PluginInterface.h"
22351290Sdim#include "lldb/Core/ThreadSafeDenseMap.h"
23351290Sdim#include "lldb/Symbol/CompilerType.h"
24351290Sdim#include "lldb/Symbol/Type.h"
25351290Sdim#include "lldb/Target/LanguageRuntime.h"
26351290Sdim#include "lldb/lldb-private.h"
27351290Sdim
28351290Sdimclass CommandObjectObjC_ClassTable_Dump;
29351290Sdim
30351290Sdimnamespace lldb_private {
31351290Sdim
32351290Sdimclass UtilityFunction;
33351290Sdim
34351290Sdimclass ObjCLanguageRuntime : public LanguageRuntime {
35351290Sdimpublic:
36351290Sdim  enum class ObjCRuntimeVersions {
37351290Sdim    eObjC_VersionUnknown = 0,
38351290Sdim    eAppleObjC_V1 = 1,
39351290Sdim    eAppleObjC_V2 = 2
40351290Sdim  };
41351290Sdim
42351290Sdim  typedef lldb::addr_t ObjCISA;
43351290Sdim
44351290Sdim  class ClassDescriptor;
45351290Sdim  typedef std::shared_ptr<ClassDescriptor> ClassDescriptorSP;
46351290Sdim
47351290Sdim  // the information that we want to support retrieving from an ObjC class this
48351290Sdim  // needs to be pure virtual since there are at least 2 different
49351290Sdim  // implementations of the runtime, and more might come
50351290Sdim  class ClassDescriptor {
51351290Sdim  public:
52351290Sdim    ClassDescriptor()
53351290Sdim        : m_is_kvo(eLazyBoolCalculate), m_is_cf(eLazyBoolCalculate),
54351290Sdim          m_type_wp() {}
55351290Sdim
56351290Sdim    virtual ~ClassDescriptor() = default;
57351290Sdim
58351290Sdim    virtual ConstString GetClassName() = 0;
59351290Sdim
60351290Sdim    virtual ClassDescriptorSP GetSuperclass() = 0;
61351290Sdim
62351290Sdim    virtual ClassDescriptorSP GetMetaclass() const = 0;
63351290Sdim
64351290Sdim    // virtual if any implementation has some other version-specific rules but
65351290Sdim    // for the known v1/v2 this is all that needs to be done
66351290Sdim    virtual bool IsKVO() {
67351290Sdim      if (m_is_kvo == eLazyBoolCalculate) {
68351290Sdim        const char *class_name = GetClassName().AsCString();
69351290Sdim        if (class_name && *class_name)
70351290Sdim          m_is_kvo =
71351290Sdim              (LazyBool)(strstr(class_name, "NSKVONotifying_") == class_name);
72351290Sdim      }
73351290Sdim      return (m_is_kvo == eLazyBoolYes);
74351290Sdim    }
75351290Sdim
76351290Sdim    // virtual if any implementation has some other version-specific rules but
77351290Sdim    // for the known v1/v2 this is all that needs to be done
78351290Sdim    virtual bool IsCFType() {
79351290Sdim      if (m_is_cf == eLazyBoolCalculate) {
80351290Sdim        const char *class_name = GetClassName().AsCString();
81351290Sdim        if (class_name && *class_name)
82351290Sdim          m_is_cf = (LazyBool)(strcmp(class_name, "__NSCFType") == 0 ||
83351290Sdim                               strcmp(class_name, "NSCFType") == 0);
84351290Sdim      }
85351290Sdim      return (m_is_cf == eLazyBoolYes);
86351290Sdim    }
87351290Sdim
88351290Sdim    virtual bool IsValid() = 0;
89351290Sdim
90351290Sdim    virtual bool GetTaggedPointerInfo(uint64_t *info_bits = nullptr,
91351290Sdim                                      uint64_t *value_bits = nullptr,
92351290Sdim                                      uint64_t *payload = nullptr) = 0;
93351290Sdim
94351290Sdim    virtual uint64_t GetInstanceSize() = 0;
95351290Sdim
96351290Sdim    // use to implement version-specific additional constraints on pointers
97351290Sdim    virtual bool CheckPointer(lldb::addr_t value, uint32_t ptr_size) const {
98351290Sdim      return true;
99351290Sdim    }
100351290Sdim
101351290Sdim    virtual ObjCISA GetISA() = 0;
102351290Sdim
103351290Sdim    // This should return true iff the interface could be completed
104351290Sdim    virtual bool
105351290Sdim    Describe(std::function<void(ObjCISA)> const &superclass_func,
106351290Sdim             std::function<bool(const char *, const char *)> const
107351290Sdim                 &instance_method_func,
108351290Sdim             std::function<bool(const char *, const char *)> const
109351290Sdim                 &class_method_func,
110351290Sdim             std::function<bool(const char *, const char *, lldb::addr_t,
111351290Sdim                                uint64_t)> const &ivar_func) const {
112351290Sdim      return false;
113351290Sdim    }
114351290Sdim
115351290Sdim    lldb::TypeSP GetType() { return m_type_wp.lock(); }
116351290Sdim
117351290Sdim    void SetType(const lldb::TypeSP &type_sp) { m_type_wp = type_sp; }
118351290Sdim
119351290Sdim    struct iVarDescriptor {
120351290Sdim      ConstString m_name;
121351290Sdim      CompilerType m_type;
122351290Sdim      uint64_t m_size;
123351290Sdim      int32_t m_offset;
124351290Sdim    };
125351290Sdim
126351290Sdim    virtual size_t GetNumIVars() { return 0; }
127351290Sdim
128351290Sdim    virtual iVarDescriptor GetIVarAtIndex(size_t idx) {
129351290Sdim      return iVarDescriptor();
130351290Sdim    }
131351290Sdim
132351290Sdim  protected:
133351290Sdim    bool IsPointerValid(lldb::addr_t value, uint32_t ptr_size,
134351290Sdim                        bool allow_NULLs = false, bool allow_tagged = false,
135351290Sdim                        bool check_version_specific = false) const;
136351290Sdim
137351290Sdim  private:
138351290Sdim    LazyBool m_is_kvo;
139351290Sdim    LazyBool m_is_cf;
140351290Sdim    lldb::TypeWP m_type_wp;
141351290Sdim  };
142351290Sdim
143351290Sdim  class EncodingToType {
144351290Sdim  public:
145351290Sdim    virtual ~EncodingToType();
146351290Sdim
147351290Sdim    virtual CompilerType RealizeType(ClangASTContext &ast_ctx, const char *name,
148360784Sdim                                     bool for_expression) = 0;
149351290Sdim    virtual CompilerType RealizeType(const char *name, bool for_expression);
150351290Sdim
151351290Sdim  protected:
152351290Sdim    std::unique_ptr<ClangASTContext> m_scratch_ast_ctx_up;
153351290Sdim  };
154351290Sdim
155351290Sdim  class ObjCExceptionPrecondition : public BreakpointPrecondition {
156351290Sdim  public:
157351290Sdim    ObjCExceptionPrecondition();
158351290Sdim
159351290Sdim    ~ObjCExceptionPrecondition() override = default;
160351290Sdim
161351290Sdim    bool EvaluatePrecondition(StoppointCallbackContext &context) override;
162351290Sdim    void GetDescription(Stream &stream, lldb::DescriptionLevel level) override;
163351290Sdim    Status ConfigurePrecondition(Args &args) override;
164351290Sdim
165351290Sdim  protected:
166351290Sdim    void AddClassName(const char *class_name);
167351290Sdim
168351290Sdim  private:
169351290Sdim    std::unordered_set<std::string> m_class_names;
170351290Sdim  };
171351290Sdim
172351290Sdim  static lldb::BreakpointPreconditionSP
173351290Sdim  GetBreakpointExceptionPrecondition(lldb::LanguageType language,
174351290Sdim                                     bool throw_bp);
175351290Sdim
176351290Sdim  class TaggedPointerVendor {
177351290Sdim  public:
178351290Sdim    virtual ~TaggedPointerVendor() = default;
179351290Sdim
180351290Sdim    virtual bool IsPossibleTaggedPointer(lldb::addr_t ptr) = 0;
181351290Sdim
182351290Sdim    virtual ObjCLanguageRuntime::ClassDescriptorSP
183351290Sdim    GetClassDescriptor(lldb::addr_t ptr) = 0;
184351290Sdim
185351290Sdim  protected:
186351290Sdim    TaggedPointerVendor() = default;
187351290Sdim
188351290Sdim  private:
189351290Sdim    DISALLOW_COPY_AND_ASSIGN(TaggedPointerVendor);
190351290Sdim  };
191351290Sdim
192351290Sdim  ~ObjCLanguageRuntime() override;
193351290Sdim
194351290Sdim  static char ID;
195351290Sdim
196351290Sdim  bool isA(const void *ClassID) const override {
197351290Sdim    return ClassID == &ID || LanguageRuntime::isA(ClassID);
198351290Sdim  }
199351290Sdim
200351290Sdim  static bool classof(const LanguageRuntime *runtime) {
201351290Sdim    return runtime->isA(&ID);
202351290Sdim  }
203351290Sdim
204351290Sdim  static ObjCLanguageRuntime *Get(Process &process) {
205351290Sdim    return llvm::cast_or_null<ObjCLanguageRuntime>(
206351290Sdim        process.GetLanguageRuntime(lldb::eLanguageTypeObjC));
207351290Sdim  }
208351290Sdim
209351290Sdim  virtual TaggedPointerVendor *GetTaggedPointerVendor() { return nullptr; }
210351290Sdim
211351290Sdim  typedef std::shared_ptr<EncodingToType> EncodingToTypeSP;
212351290Sdim
213351290Sdim  virtual EncodingToTypeSP GetEncodingToType();
214351290Sdim
215351290Sdim  virtual ClassDescriptorSP GetClassDescriptor(ValueObject &in_value);
216351290Sdim
217351290Sdim  ClassDescriptorSP GetNonKVOClassDescriptor(ValueObject &in_value);
218351290Sdim
219351290Sdim  virtual ClassDescriptorSP
220351290Sdim  GetClassDescriptorFromClassName(ConstString class_name);
221351290Sdim
222351290Sdim  virtual ClassDescriptorSP GetClassDescriptorFromISA(ObjCISA isa);
223351290Sdim
224351290Sdim  ClassDescriptorSP GetNonKVOClassDescriptor(ObjCISA isa);
225351290Sdim
226351290Sdim  lldb::LanguageType GetLanguageType() const override {
227351290Sdim    return lldb::eLanguageTypeObjC;
228351290Sdim  }
229351290Sdim
230351290Sdim  virtual bool IsModuleObjCLibrary(const lldb::ModuleSP &module_sp) = 0;
231351290Sdim
232351290Sdim  virtual bool ReadObjCLibrary(const lldb::ModuleSP &module_sp) = 0;
233351290Sdim
234351290Sdim  virtual bool HasReadObjCLibrary() = 0;
235351290Sdim
236351290Sdim  lldb::addr_t LookupInMethodCache(lldb::addr_t class_addr, lldb::addr_t sel);
237351290Sdim
238351290Sdim  void AddToMethodCache(lldb::addr_t class_addr, lldb::addr_t sel,
239351290Sdim                        lldb::addr_t impl_addr);
240351290Sdim
241351290Sdim  TypeAndOrName LookupInClassNameCache(lldb::addr_t class_addr);
242351290Sdim
243351290Sdim  void AddToClassNameCache(lldb::addr_t class_addr, const char *name,
244351290Sdim                           lldb::TypeSP type_sp);
245351290Sdim
246351290Sdim  void AddToClassNameCache(lldb::addr_t class_addr,
247351290Sdim                           const TypeAndOrName &class_or_type_name);
248351290Sdim
249351290Sdim  lldb::TypeSP LookupInCompleteClassCache(ConstString &name);
250351290Sdim
251351290Sdim  llvm::Optional<CompilerType> GetRuntimeType(CompilerType base_type) override;
252351290Sdim
253351290Sdim  virtual UtilityFunction *CreateObjectChecker(const char *) = 0;
254351290Sdim
255351290Sdim  virtual ObjCRuntimeVersions GetRuntimeVersion() const {
256351290Sdim    return ObjCRuntimeVersions::eObjC_VersionUnknown;
257351290Sdim  }
258351290Sdim
259351290Sdim  bool IsValidISA(ObjCISA isa) {
260351290Sdim    UpdateISAToDescriptorMap();
261351290Sdim    return m_isa_to_descriptor.count(isa) > 0;
262351290Sdim  }
263351290Sdim
264351290Sdim  virtual void UpdateISAToDescriptorMapIfNeeded() = 0;
265351290Sdim
266351290Sdim  void UpdateISAToDescriptorMap() {
267351290Sdim    if (m_process && m_process->GetStopID() != m_isa_to_descriptor_stop_id) {
268351290Sdim      UpdateISAToDescriptorMapIfNeeded();
269351290Sdim    }
270351290Sdim  }
271351290Sdim
272351290Sdim  virtual ObjCISA GetISA(ConstString name);
273351290Sdim
274351290Sdim  virtual ObjCISA GetParentClass(ObjCISA isa);
275351290Sdim
276351290Sdim  // Finds the byte offset of the child_type ivar in parent_type.  If it can't
277351290Sdim  // find the offset, returns LLDB_INVALID_IVAR_OFFSET.
278351290Sdim
279351290Sdim  virtual size_t GetByteOffsetForIvar(CompilerType &parent_qual_type,
280351290Sdim                                      const char *ivar_name);
281351290Sdim
282351290Sdim  bool HasNewLiteralsAndIndexing() {
283351290Sdim    if (m_has_new_literals_and_indexing == eLazyBoolCalculate) {
284351290Sdim      if (CalculateHasNewLiteralsAndIndexing())
285351290Sdim        m_has_new_literals_and_indexing = eLazyBoolYes;
286351290Sdim      else
287351290Sdim        m_has_new_literals_and_indexing = eLazyBoolNo;
288351290Sdim    }
289351290Sdim
290351290Sdim    return (m_has_new_literals_and_indexing == eLazyBoolYes);
291351290Sdim  }
292351290Sdim
293351290Sdim  void SymbolsDidLoad(const ModuleList &module_list) override {
294351290Sdim    m_negative_complete_class_cache.clear();
295351290Sdim  }
296351290Sdim
297351290Sdim  bool GetTypeBitSize(const CompilerType &compiler_type,
298351290Sdim                      uint64_t &size) override;
299351290Sdim
300351290Sdim  /// Check whether the name is "self" or "_cmd" and should show up in
301351290Sdim  /// "frame variable".
302351290Sdim  bool IsWhitelistedRuntimeValue(ConstString name) override;
303351290Sdim
304351290Sdimprotected:
305351290Sdim  // Classes that inherit from ObjCLanguageRuntime can see and modify these
306351290Sdim  ObjCLanguageRuntime(Process *process);
307351290Sdim
308351290Sdim  virtual bool CalculateHasNewLiteralsAndIndexing() { return false; }
309351290Sdim
310351290Sdim  bool ISAIsCached(ObjCISA isa) const {
311351290Sdim    return m_isa_to_descriptor.find(isa) != m_isa_to_descriptor.end();
312351290Sdim  }
313351290Sdim
314351290Sdim  bool AddClass(ObjCISA isa, const ClassDescriptorSP &descriptor_sp) {
315351290Sdim    if (isa != 0) {
316351290Sdim      m_isa_to_descriptor[isa] = descriptor_sp;
317351290Sdim      return true;
318351290Sdim    }
319351290Sdim    return false;
320351290Sdim  }
321351290Sdim
322351290Sdim  bool AddClass(ObjCISA isa, const ClassDescriptorSP &descriptor_sp,
323351290Sdim                const char *class_name);
324351290Sdim
325351290Sdim  bool AddClass(ObjCISA isa, const ClassDescriptorSP &descriptor_sp,
326351290Sdim                uint32_t class_name_hash) {
327351290Sdim    if (isa != 0) {
328351290Sdim      m_isa_to_descriptor[isa] = descriptor_sp;
329351290Sdim      m_hash_to_isa_map.insert(std::make_pair(class_name_hash, isa));
330351290Sdim      return true;
331351290Sdim    }
332351290Sdim    return false;
333351290Sdim  }
334351290Sdim
335351290Sdimprivate:
336351290Sdim  // We keep a map of <Class,Selector>->Implementation so we don't have to call
337351290Sdim  // the resolver function over and over.
338351290Sdim
339351290Sdim  // FIXME: We need to watch for the loading of Protocols, and flush the cache
340351290Sdim  // for any
341351290Sdim  // class that we see so changed.
342351290Sdim
343351290Sdim  struct ClassAndSel {
344351290Sdim    ClassAndSel() {
345351290Sdim      sel_addr = LLDB_INVALID_ADDRESS;
346351290Sdim      class_addr = LLDB_INVALID_ADDRESS;
347351290Sdim    }
348351290Sdim
349351290Sdim    ClassAndSel(lldb::addr_t in_sel_addr, lldb::addr_t in_class_addr)
350351290Sdim        : class_addr(in_class_addr), sel_addr(in_sel_addr) {}
351351290Sdim
352351290Sdim    bool operator==(const ClassAndSel &rhs) {
353351290Sdim      if (class_addr == rhs.class_addr && sel_addr == rhs.sel_addr)
354351290Sdim        return true;
355351290Sdim      else
356351290Sdim        return false;
357351290Sdim    }
358351290Sdim
359351290Sdim    bool operator<(const ClassAndSel &rhs) const {
360351290Sdim      if (class_addr < rhs.class_addr)
361351290Sdim        return true;
362351290Sdim      else if (class_addr > rhs.class_addr)
363351290Sdim        return false;
364351290Sdim      else {
365351290Sdim        if (sel_addr < rhs.sel_addr)
366351290Sdim          return true;
367351290Sdim        else
368351290Sdim          return false;
369351290Sdim      }
370351290Sdim    }
371351290Sdim
372351290Sdim    lldb::addr_t class_addr;
373351290Sdim    lldb::addr_t sel_addr;
374351290Sdim  };
375351290Sdim
376351290Sdim  typedef std::map<ClassAndSel, lldb::addr_t> MsgImplMap;
377351290Sdim  typedef std::map<ObjCISA, ClassDescriptorSP> ISAToDescriptorMap;
378351290Sdim  typedef std::multimap<uint32_t, ObjCISA> HashToISAMap;
379351290Sdim  typedef ISAToDescriptorMap::iterator ISAToDescriptorIterator;
380351290Sdim  typedef HashToISAMap::iterator HashToISAIterator;
381351290Sdim  typedef ThreadSafeDenseMap<void *, uint64_t> TypeSizeCache;
382351290Sdim
383351290Sdim  MsgImplMap m_impl_cache;
384351290Sdim  LazyBool m_has_new_literals_and_indexing;
385351290Sdim  ISAToDescriptorMap m_isa_to_descriptor;
386351290Sdim  HashToISAMap m_hash_to_isa_map;
387351290Sdim  TypeSizeCache m_type_size_cache;
388351290Sdim
389351290Sdimprotected:
390351290Sdim  uint32_t m_isa_to_descriptor_stop_id;
391351290Sdim
392351290Sdim  typedef std::map<ConstString, lldb::TypeWP> CompleteClassMap;
393351290Sdim  CompleteClassMap m_complete_class_cache;
394351290Sdim
395351290Sdim  struct ConstStringSetHelpers {
396351290Sdim    size_t operator()(ConstString arg) const // for hashing
397351290Sdim    {
398351290Sdim      return (size_t)arg.GetCString();
399351290Sdim    }
400351290Sdim    bool operator()(ConstString arg1,
401351290Sdim                    ConstString arg2) const // for equality
402351290Sdim    {
403351290Sdim      return arg1.operator==(arg2);
404351290Sdim    }
405351290Sdim  };
406351290Sdim  typedef std::unordered_set<ConstString, ConstStringSetHelpers,
407351290Sdim                             ConstStringSetHelpers>
408351290Sdim      CompleteClassSet;
409351290Sdim  CompleteClassSet m_negative_complete_class_cache;
410351290Sdim
411351290Sdim  ISAToDescriptorIterator GetDescriptorIterator(ConstString name);
412351290Sdim
413351290Sdim  friend class ::CommandObjectObjC_ClassTable_Dump;
414351290Sdim
415351290Sdim  std::pair<ISAToDescriptorIterator, ISAToDescriptorIterator>
416351290Sdim  GetDescriptorIteratorPair(bool update_if_needed = true);
417351290Sdim
418351290Sdim  void ReadObjCLibraryIfNeeded(const ModuleList &module_list);
419351290Sdim
420351290Sdim  DISALLOW_COPY_AND_ASSIGN(ObjCLanguageRuntime);
421351290Sdim};
422351290Sdim
423351290Sdim} // namespace lldb_private
424351290Sdim
425351290Sdim#endif // liblldb_ObjCLanguageRuntime_h_
426