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