1//===-- ObjCLanguageRuntime.cpp ---------------------------------*- C++ -*-===//
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-exception
6//
7//===----------------------------------------------------------------------===//
8#include "clang/AST/Type.h"
9
10#include "ObjCLanguageRuntime.h"
11
12#include "lldb/Core/MappedHash.h"
13#include "lldb/Core/Module.h"
14#include "lldb/Core/PluginManager.h"
15#include "lldb/Core/ValueObject.h"
16#include "lldb/Symbol/ClangASTContext.h"
17#include "lldb/Symbol/SymbolContext.h"
18#include "lldb/Symbol/SymbolFile.h"
19#include "lldb/Symbol/Type.h"
20#include "lldb/Symbol/TypeList.h"
21#include "lldb/Symbol/Variable.h"
22#include "lldb/Target/Target.h"
23#include "lldb/Utility/Log.h"
24#include "lldb/Utility/Timer.h"
25
26#include "llvm/ADT/StringRef.h"
27#include "llvm/Support/DJB.h"
28
29using namespace lldb;
30using namespace lldb_private;
31
32char ObjCLanguageRuntime::ID = 0;
33
34// Destructor
35ObjCLanguageRuntime::~ObjCLanguageRuntime() {}
36
37ObjCLanguageRuntime::ObjCLanguageRuntime(Process *process)
38    : LanguageRuntime(process), m_impl_cache(),
39      m_has_new_literals_and_indexing(eLazyBoolCalculate),
40      m_isa_to_descriptor(), m_hash_to_isa_map(), m_type_size_cache(),
41      m_isa_to_descriptor_stop_id(UINT32_MAX), m_complete_class_cache(),
42      m_negative_complete_class_cache() {}
43
44bool ObjCLanguageRuntime::IsWhitelistedRuntimeValue(ConstString name) {
45  static ConstString g_self = ConstString("self");
46  static ConstString g_cmd = ConstString("_cmd");
47  return name == g_self || name == g_cmd;
48}
49
50bool ObjCLanguageRuntime::AddClass(ObjCISA isa,
51                                   const ClassDescriptorSP &descriptor_sp,
52                                   const char *class_name) {
53  if (isa != 0) {
54    m_isa_to_descriptor[isa] = descriptor_sp;
55    // class_name is assumed to be valid
56    m_hash_to_isa_map.insert(std::make_pair(llvm::djbHash(class_name), isa));
57    return true;
58  }
59  return false;
60}
61
62void ObjCLanguageRuntime::AddToMethodCache(lldb::addr_t class_addr,
63                                           lldb::addr_t selector,
64                                           lldb::addr_t impl_addr) {
65  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP));
66  if (log) {
67    LLDB_LOGF(log,
68              "Caching: class 0x%" PRIx64 " selector 0x%" PRIx64
69              " implementation 0x%" PRIx64 ".",
70              class_addr, selector, impl_addr);
71  }
72  m_impl_cache.insert(std::pair<ClassAndSel, lldb::addr_t>(
73      ClassAndSel(class_addr, selector), impl_addr));
74}
75
76lldb::addr_t ObjCLanguageRuntime::LookupInMethodCache(lldb::addr_t class_addr,
77                                                      lldb::addr_t selector) {
78  MsgImplMap::iterator pos, end = m_impl_cache.end();
79  pos = m_impl_cache.find(ClassAndSel(class_addr, selector));
80  if (pos != end)
81    return (*pos).second;
82  return LLDB_INVALID_ADDRESS;
83}
84
85lldb::TypeSP
86ObjCLanguageRuntime::LookupInCompleteClassCache(ConstString &name) {
87  CompleteClassMap::iterator complete_class_iter =
88      m_complete_class_cache.find(name);
89
90  if (complete_class_iter != m_complete_class_cache.end()) {
91    // Check the weak pointer to make sure the type hasn't been unloaded
92    TypeSP complete_type_sp(complete_class_iter->second.lock());
93
94    if (complete_type_sp)
95      return complete_type_sp;
96    else
97      m_complete_class_cache.erase(name);
98  }
99
100  if (m_negative_complete_class_cache.count(name) > 0)
101    return TypeSP();
102
103  const ModuleList &modules = m_process->GetTarget().GetImages();
104
105  SymbolContextList sc_list;
106  modules.FindSymbolsWithNameAndType(name, eSymbolTypeObjCClass, sc_list);
107  const size_t matching_symbols = sc_list.GetSize();
108
109  if (matching_symbols) {
110    SymbolContext sc;
111
112    sc_list.GetContextAtIndex(0, sc);
113
114    ModuleSP module_sp(sc.module_sp);
115
116    if (!module_sp)
117      return TypeSP();
118
119    const bool exact_match = true;
120    const uint32_t max_matches = UINT32_MAX;
121    TypeList types;
122
123    llvm::DenseSet<SymbolFile *> searched_symbol_files;
124    module_sp->FindTypes(name, exact_match, max_matches, searched_symbol_files,
125                         types);
126
127    for (uint32_t i = 0; i < types.GetSize(); ++i) {
128      TypeSP type_sp(types.GetTypeAtIndex(i));
129
130      if (ClangASTContext::IsObjCObjectOrInterfaceType(
131              type_sp->GetForwardCompilerType())) {
132        if (type_sp->IsCompleteObjCClass()) {
133          m_complete_class_cache[name] = type_sp;
134          return type_sp;
135        }
136      }
137    }
138  }
139  m_negative_complete_class_cache.insert(name);
140  return TypeSP();
141}
142
143size_t ObjCLanguageRuntime::GetByteOffsetForIvar(CompilerType &parent_qual_type,
144                                                 const char *ivar_name) {
145  return LLDB_INVALID_IVAR_OFFSET;
146}
147
148bool ObjCLanguageRuntime::ClassDescriptor::IsPointerValid(
149    lldb::addr_t value, uint32_t ptr_size, bool allow_NULLs, bool allow_tagged,
150    bool check_version_specific) const {
151  if (!value)
152    return allow_NULLs;
153  if ((value % 2) == 1 && allow_tagged)
154    return true;
155  if ((value % ptr_size) == 0)
156    return (check_version_specific ? CheckPointer(value, ptr_size) : true);
157  else
158    return false;
159}
160
161ObjCLanguageRuntime::ObjCISA
162ObjCLanguageRuntime::GetISA(ConstString name) {
163  ISAToDescriptorIterator pos = GetDescriptorIterator(name);
164  if (pos != m_isa_to_descriptor.end())
165    return pos->first;
166  return 0;
167}
168
169ObjCLanguageRuntime::ISAToDescriptorIterator
170ObjCLanguageRuntime::GetDescriptorIterator(ConstString name) {
171  ISAToDescriptorIterator end = m_isa_to_descriptor.end();
172
173  if (name) {
174    UpdateISAToDescriptorMap();
175    if (m_hash_to_isa_map.empty()) {
176      // No name hashes were provided, we need to just linearly power through
177      // the names and find a match
178      for (ISAToDescriptorIterator pos = m_isa_to_descriptor.begin();
179           pos != end; ++pos) {
180        if (pos->second->GetClassName() == name)
181          return pos;
182      }
183    } else {
184      // Name hashes were provided, so use them to efficiently lookup name to
185      // isa/descriptor
186      const uint32_t name_hash = llvm::djbHash(name.GetStringRef());
187      std::pair<HashToISAIterator, HashToISAIterator> range =
188          m_hash_to_isa_map.equal_range(name_hash);
189      for (HashToISAIterator range_pos = range.first; range_pos != range.second;
190           ++range_pos) {
191        ISAToDescriptorIterator pos =
192            m_isa_to_descriptor.find(range_pos->second);
193        if (pos != m_isa_to_descriptor.end()) {
194          if (pos->second->GetClassName() == name)
195            return pos;
196        }
197      }
198    }
199  }
200  return end;
201}
202
203std::pair<ObjCLanguageRuntime::ISAToDescriptorIterator,
204          ObjCLanguageRuntime::ISAToDescriptorIterator>
205ObjCLanguageRuntime::GetDescriptorIteratorPair(bool update_if_needed) {
206  if (update_if_needed)
207    UpdateISAToDescriptorMapIfNeeded();
208
209  return std::pair<ObjCLanguageRuntime::ISAToDescriptorIterator,
210                   ObjCLanguageRuntime::ISAToDescriptorIterator>(
211      m_isa_to_descriptor.begin(), m_isa_to_descriptor.end());
212}
213
214ObjCLanguageRuntime::ObjCISA
215ObjCLanguageRuntime::GetParentClass(ObjCLanguageRuntime::ObjCISA isa) {
216  ClassDescriptorSP objc_class_sp(GetClassDescriptorFromISA(isa));
217  if (objc_class_sp) {
218    ClassDescriptorSP objc_super_class_sp(objc_class_sp->GetSuperclass());
219    if (objc_super_class_sp)
220      return objc_super_class_sp->GetISA();
221  }
222  return 0;
223}
224
225ObjCLanguageRuntime::ClassDescriptorSP
226ObjCLanguageRuntime::GetClassDescriptorFromClassName(
227    ConstString class_name) {
228  ISAToDescriptorIterator pos = GetDescriptorIterator(class_name);
229  if (pos != m_isa_to_descriptor.end())
230    return pos->second;
231  return ClassDescriptorSP();
232}
233
234ObjCLanguageRuntime::ClassDescriptorSP
235ObjCLanguageRuntime::GetClassDescriptor(ValueObject &valobj) {
236  ClassDescriptorSP objc_class_sp;
237  // if we get an invalid VO (which might still happen when playing around with
238  // pointers returned by the expression parser, don't consider this a valid
239  // ObjC object)
240  if (valobj.GetCompilerType().IsValid()) {
241    addr_t isa_pointer = valobj.GetPointerValue();
242    if (isa_pointer != LLDB_INVALID_ADDRESS) {
243      ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
244
245      Process *process = exe_ctx.GetProcessPtr();
246      if (process) {
247        Status error;
248        ObjCISA isa = process->ReadPointerFromMemory(isa_pointer, error);
249        if (isa != LLDB_INVALID_ADDRESS)
250          objc_class_sp = GetClassDescriptorFromISA(isa);
251      }
252    }
253  }
254  return objc_class_sp;
255}
256
257ObjCLanguageRuntime::ClassDescriptorSP
258ObjCLanguageRuntime::GetNonKVOClassDescriptor(ValueObject &valobj) {
259  ObjCLanguageRuntime::ClassDescriptorSP objc_class_sp(
260      GetClassDescriptor(valobj));
261  if (objc_class_sp) {
262    if (!objc_class_sp->IsKVO())
263      return objc_class_sp;
264
265    ClassDescriptorSP non_kvo_objc_class_sp(objc_class_sp->GetSuperclass());
266    if (non_kvo_objc_class_sp && non_kvo_objc_class_sp->IsValid())
267      return non_kvo_objc_class_sp;
268  }
269  return ClassDescriptorSP();
270}
271
272ObjCLanguageRuntime::ClassDescriptorSP
273ObjCLanguageRuntime::GetClassDescriptorFromISA(ObjCISA isa) {
274  if (isa) {
275    UpdateISAToDescriptorMap();
276    ObjCLanguageRuntime::ISAToDescriptorIterator pos =
277        m_isa_to_descriptor.find(isa);
278    if (pos != m_isa_to_descriptor.end())
279      return pos->second;
280  }
281  return ClassDescriptorSP();
282}
283
284ObjCLanguageRuntime::ClassDescriptorSP
285ObjCLanguageRuntime::GetNonKVOClassDescriptor(ObjCISA isa) {
286  if (isa) {
287    ClassDescriptorSP objc_class_sp = GetClassDescriptorFromISA(isa);
288    if (objc_class_sp && objc_class_sp->IsValid()) {
289      if (!objc_class_sp->IsKVO())
290        return objc_class_sp;
291
292      ClassDescriptorSP non_kvo_objc_class_sp(objc_class_sp->GetSuperclass());
293      if (non_kvo_objc_class_sp && non_kvo_objc_class_sp->IsValid())
294        return non_kvo_objc_class_sp;
295    }
296  }
297  return ClassDescriptorSP();
298}
299
300CompilerType
301ObjCLanguageRuntime::EncodingToType::RealizeType(const char *name,
302                                                 bool for_expression) {
303  if (m_scratch_ast_ctx_up)
304    return RealizeType(*m_scratch_ast_ctx_up, name, for_expression);
305  return CompilerType();
306}
307
308ObjCLanguageRuntime::EncodingToType::~EncodingToType() {}
309
310ObjCLanguageRuntime::EncodingToTypeSP ObjCLanguageRuntime::GetEncodingToType() {
311  return nullptr;
312}
313
314bool ObjCLanguageRuntime::GetTypeBitSize(const CompilerType &compiler_type,
315                                         uint64_t &size) {
316  void *opaque_ptr = compiler_type.GetOpaqueQualType();
317  size = m_type_size_cache.Lookup(opaque_ptr);
318  // an ObjC object will at least have an ISA, so 0 is definitely not OK
319  if (size > 0)
320    return true;
321
322  ClassDescriptorSP class_descriptor_sp =
323      GetClassDescriptorFromClassName(compiler_type.GetTypeName());
324  if (!class_descriptor_sp)
325    return false;
326
327  int32_t max_offset = INT32_MIN;
328  uint64_t sizeof_max = 0;
329  bool found = false;
330
331  for (size_t idx = 0; idx < class_descriptor_sp->GetNumIVars(); idx++) {
332    const auto &ivar = class_descriptor_sp->GetIVarAtIndex(idx);
333    int32_t cur_offset = ivar.m_offset;
334    if (cur_offset > max_offset) {
335      max_offset = cur_offset;
336      sizeof_max = ivar.m_size;
337      found = true;
338    }
339  }
340
341  size = 8 * (max_offset + sizeof_max);
342  if (found)
343    m_type_size_cache.Insert(opaque_ptr, size);
344
345  return found;
346}
347
348lldb::BreakpointPreconditionSP
349ObjCLanguageRuntime::GetBreakpointExceptionPrecondition(LanguageType language,
350                                                        bool throw_bp) {
351  if (language != eLanguageTypeObjC)
352    return lldb::BreakpointPreconditionSP();
353  if (!throw_bp)
354    return lldb::BreakpointPreconditionSP();
355  BreakpointPreconditionSP precondition_sp(
356      new ObjCLanguageRuntime::ObjCExceptionPrecondition());
357  return precondition_sp;
358}
359
360// Exception breakpoint Precondition class for ObjC:
361void ObjCLanguageRuntime::ObjCExceptionPrecondition::AddClassName(
362    const char *class_name) {
363  m_class_names.insert(class_name);
364}
365
366ObjCLanguageRuntime::ObjCExceptionPrecondition::ObjCExceptionPrecondition() {}
367
368bool ObjCLanguageRuntime::ObjCExceptionPrecondition::EvaluatePrecondition(
369    StoppointCallbackContext &context) {
370  return true;
371}
372
373void ObjCLanguageRuntime::ObjCExceptionPrecondition::GetDescription(
374    Stream &stream, lldb::DescriptionLevel level) {}
375
376Status ObjCLanguageRuntime::ObjCExceptionPrecondition::ConfigurePrecondition(
377    Args &args) {
378  Status error;
379  if (args.GetArgumentCount() > 0)
380    error.SetErrorString(
381        "The ObjC Exception breakpoint doesn't support extra options.");
382  return error;
383}
384
385llvm::Optional<CompilerType>
386ObjCLanguageRuntime::GetRuntimeType(CompilerType base_type) {
387  CompilerType class_type;
388  bool is_pointer_type = false;
389
390  if (ClangASTContext::IsObjCObjectPointerType(base_type, &class_type))
391    is_pointer_type = true;
392  else if (ClangASTContext::IsObjCObjectOrInterfaceType(base_type))
393    class_type = base_type;
394  else
395    return llvm::None;
396
397  if (!class_type)
398    return llvm::None;
399
400  ConstString class_name(class_type.GetConstTypeName());
401  if (!class_name)
402    return llvm::None;
403
404  TypeSP complete_objc_class_type_sp = LookupInCompleteClassCache(class_name);
405  if (!complete_objc_class_type_sp)
406    return llvm::None;
407
408  CompilerType complete_class(
409      complete_objc_class_type_sp->GetFullCompilerType());
410  if (complete_class.GetCompleteType()) {
411    if (is_pointer_type)
412      return complete_class.GetPointerType();
413    else
414      return complete_class;
415  }
416
417  return llvm::None;
418}
419