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