1//===-- ObjCLanguageRuntime.cpp ---------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9#include "clang/AST/Type.h"
10
11#include "lldb/Core/Log.h"
12#include "lldb/Core/MappedHash.h"
13#include "lldb/Core/Module.h"
14#include "lldb/Core/PluginManager.h"
15#include "lldb/Core/Timer.h"
16#include "lldb/Core/ValueObject.h"
17#include "lldb/Symbol/ClangASTContext.h"
18#include "lldb/Symbol/Type.h"
19#include "lldb/Symbol/TypeList.h"
20#include "lldb/Target/ObjCLanguageRuntime.h"
21#include "lldb/Target/Target.h"
22
23#include "llvm/ADT/StringRef.h"
24
25using namespace lldb;
26using namespace lldb_private;
27
28//----------------------------------------------------------------------
29// Destructor
30//----------------------------------------------------------------------
31ObjCLanguageRuntime::~ObjCLanguageRuntime()
32{
33}
34
35ObjCLanguageRuntime::ObjCLanguageRuntime (Process *process) :
36    LanguageRuntime (process),
37    m_has_new_literals_and_indexing (eLazyBoolCalculate),
38    m_isa_to_descriptor(),
39    m_isa_to_descriptor_stop_id (UINT32_MAX)
40{
41
42}
43
44bool
45ObjCLanguageRuntime::AddClass (ObjCISA isa, const ClassDescriptorSP &descriptor_sp, const char *class_name)
46{
47    if (isa != 0)
48    {
49        m_isa_to_descriptor[isa] = descriptor_sp;
50        // class_name is assumed to be valid
51        m_hash_to_isa_map.insert(std::make_pair(MappedHash::HashStringUsingDJB(class_name), isa));
52        return true;
53    }
54    return false;
55}
56
57void
58ObjCLanguageRuntime::AddToMethodCache (lldb::addr_t class_addr, lldb::addr_t selector, lldb::addr_t impl_addr)
59{
60    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
61    if (log)
62    {
63        log->Printf ("Caching: class 0x%" PRIx64 " selector 0x%" PRIx64 " implementation 0x%" PRIx64 ".", class_addr, selector, impl_addr);
64    }
65    m_impl_cache.insert (std::pair<ClassAndSel,lldb::addr_t> (ClassAndSel(class_addr, selector), impl_addr));
66}
67
68lldb::addr_t
69ObjCLanguageRuntime::LookupInMethodCache (lldb::addr_t class_addr, lldb::addr_t selector)
70{
71    MsgImplMap::iterator pos, end = m_impl_cache.end();
72    pos = m_impl_cache.find (ClassAndSel(class_addr, selector));
73    if (pos != end)
74        return (*pos).second;
75    return LLDB_INVALID_ADDRESS;
76}
77
78
79lldb::TypeSP
80ObjCLanguageRuntime::LookupInCompleteClassCache (ConstString &name)
81{
82    CompleteClassMap::iterator complete_class_iter = m_complete_class_cache.find(name);
83
84    if (complete_class_iter != m_complete_class_cache.end())
85    {
86        // Check the weak pointer to make sure the type hasn't been unloaded
87        TypeSP complete_type_sp (complete_class_iter->second.lock());
88
89        if (complete_type_sp)
90            return complete_type_sp;
91        else
92            m_complete_class_cache.erase(name);
93    }
94
95    if (m_negative_complete_class_cache.count(name) > 0)
96        return TypeSP();
97
98    const ModuleList &modules = m_process->GetTarget().GetImages();
99
100    SymbolContextList sc_list;
101    const size_t matching_symbols = modules.FindSymbolsWithNameAndType (name,
102                                                                        eSymbolTypeObjCClass,
103                                                                        sc_list);
104
105    if (matching_symbols)
106    {
107        SymbolContext sc;
108
109        sc_list.GetContextAtIndex(0, sc);
110
111        ModuleSP module_sp(sc.module_sp);
112
113        if (!module_sp)
114            return TypeSP();
115
116        const SymbolContext null_sc;
117        const bool exact_match = true;
118        const uint32_t max_matches = UINT32_MAX;
119        TypeList types;
120
121        const uint32_t num_types = module_sp->FindTypes (null_sc,
122                                                         name,
123                                                         exact_match,
124                                                         max_matches,
125                                                         types);
126
127        if (num_types)
128        {
129            uint32_t i;
130            for (i = 0; i < num_types; ++i)
131            {
132                TypeSP type_sp (types.GetTypeAtIndex(i));
133
134                if (type_sp->GetClangForwardType().IsObjCObjectOrInterfaceType())
135                {
136                    if (type_sp->IsCompleteObjCClass())
137                    {
138                        m_complete_class_cache[name] = type_sp;
139                        return type_sp;
140                    }
141                }
142            }
143        }
144    }
145    m_negative_complete_class_cache.insert(name);
146    return TypeSP();
147}
148
149size_t
150ObjCLanguageRuntime::GetByteOffsetForIvar (ClangASTType &parent_qual_type, const char *ivar_name)
151{
152    return LLDB_INVALID_IVAR_OFFSET;
153}
154
155void
156ObjCLanguageRuntime::MethodName::Clear()
157{
158    m_full.Clear();
159    m_class.Clear();
160    m_category.Clear();
161    m_selector.Clear();
162    m_type = eTypeUnspecified;
163    m_category_is_valid = false;
164}
165
166//bool
167//ObjCLanguageRuntime::MethodName::SetName (const char *name, bool strict)
168//{
169//    Clear();
170//    if (name && name[0])
171//    {
172//        // If "strict" is true. then the method must be specified with a
173//        // '+' or '-' at the beginning. If "strict" is false, then the '+'
174//        // or '-' can be omitted
175//        bool valid_prefix = false;
176//
177//        if (name[0] == '+' || name[0] == '-')
178//        {
179//            valid_prefix = name[1] == '[';
180//        }
181//        else if (!strict)
182//        {
183//            // "strict" is false, the name just needs to start with '['
184//            valid_prefix = name[0] == '[';
185//        }
186//
187//        if (valid_prefix)
188//        {
189//            static RegularExpression g_regex("^([-+]?)\\[([A-Za-z_][A-Za-z_0-9]*)(\\([A-Za-z_][A-Za-z_0-9]*\\))? ([A-Za-z_][A-Za-z_0-9:]*)\\]$");
190//            llvm::StringRef matches[4];
191//            // Since we are using a global regular expression, we must use the threadsafe version of execute
192//            if (g_regex.ExecuteThreadSafe(name, matches, 4))
193//            {
194//                m_full.SetCString(name);
195//                if (matches[0].empty())
196//                    m_type = eTypeUnspecified;
197//                else if (matches[0][0] == '+')
198//                    m_type = eTypeClassMethod;
199//                else
200//                    m_type = eTypeInstanceMethod;
201//                m_class.SetString(matches[1]);
202//                m_selector.SetString(matches[3]);
203//                if (!matches[2].empty())
204//                    m_category.SetString(matches[2]);
205//            }
206//        }
207//    }
208//    return IsValid(strict);
209//}
210
211bool
212ObjCLanguageRuntime::MethodName::SetName (const char *name, bool strict)
213{
214    Clear();
215    if (name && name[0])
216    {
217        // If "strict" is true. then the method must be specified with a
218        // '+' or '-' at the beginning. If "strict" is false, then the '+'
219        // or '-' can be omitted
220        bool valid_prefix = false;
221
222        if (name[0] == '+' || name[0] == '-')
223        {
224            valid_prefix = name[1] == '[';
225            if (name[0] == '+')
226                m_type = eTypeClassMethod;
227            else
228                m_type = eTypeInstanceMethod;
229        }
230        else if (!strict)
231        {
232            // "strict" is false, the name just needs to start with '['
233            valid_prefix = name[0] == '[';
234        }
235
236        if (valid_prefix)
237        {
238            int name_len = strlen (name);
239            // Objective C methods must have at least:
240            //      "-[" or "+[" prefix
241            //      One character for a class name
242            //      One character for the space between the class name
243            //      One character for the method name
244            //      "]" suffix
245            if (name_len >= (5 + (strict ? 1 : 0)) && name[name_len - 1] == ']')
246            {
247                m_full.SetCStringWithLength(name, name_len);
248            }
249        }
250    }
251    return IsValid(strict);
252}
253
254const ConstString &
255ObjCLanguageRuntime::MethodName::GetClassName ()
256{
257    if (!m_class)
258    {
259        if (IsValid(false))
260        {
261            const char *full = m_full.GetCString();
262            const char *class_start = (full[0] == '[' ? full + 1 : full + 2);
263            const char *paren_pos = strchr (class_start, '(');
264            if (paren_pos)
265            {
266                m_class.SetCStringWithLength (class_start, paren_pos - class_start);
267            }
268            else
269            {
270                // No '(' was found in the full name, we can definitively say
271                // that our category was valid (and empty).
272                m_category_is_valid = true;
273                const char *space_pos = strchr (full, ' ');
274                if (space_pos)
275                {
276                    m_class.SetCStringWithLength (class_start, space_pos - class_start);
277                    if (!m_class_category)
278                    {
279                        // No category in name, so we can also fill in the m_class_category
280                        m_class_category = m_class;
281                    }
282                }
283            }
284        }
285    }
286    return m_class;
287}
288
289const ConstString &
290ObjCLanguageRuntime::MethodName::GetClassNameWithCategory ()
291{
292    if (!m_class_category)
293    {
294        if (IsValid(false))
295        {
296            const char *full = m_full.GetCString();
297            const char *class_start = (full[0] == '[' ? full + 1 : full + 2);
298            const char *space_pos = strchr (full, ' ');
299            if (space_pos)
300            {
301                m_class_category.SetCStringWithLength (class_start, space_pos - class_start);
302                // If m_class hasn't been filled in and the class with category doesn't
303                // contain a '(', then we can also fill in the m_class
304                if (!m_class && strchr (m_class_category.GetCString(), '(') == NULL)
305                {
306                    m_class = m_class_category;
307                    // No '(' was found in the full name, we can definitively say
308                    // that our category was valid (and empty).
309                    m_category_is_valid = true;
310
311                }
312            }
313        }
314    }
315    return m_class_category;
316}
317
318const ConstString &
319ObjCLanguageRuntime::MethodName::GetSelector ()
320{
321    if (!m_selector)
322    {
323        if (IsValid(false))
324        {
325            const char *full = m_full.GetCString();
326            const char *space_pos = strchr (full, ' ');
327            if (space_pos)
328            {
329                ++space_pos; // skip the space
330                m_selector.SetCStringWithLength (space_pos, m_full.GetLength() - (space_pos - full) - 1);
331            }
332        }
333    }
334    return m_selector;
335}
336
337const ConstString &
338ObjCLanguageRuntime::MethodName::GetCategory ()
339{
340    if (!m_category_is_valid && !m_category)
341    {
342        if (IsValid(false))
343        {
344            m_category_is_valid = true;
345            const char *full = m_full.GetCString();
346            const char *class_start = (full[0] == '[' ? full + 1 : full + 2);
347            const char *open_paren_pos = strchr (class_start, '(');
348            if (open_paren_pos)
349            {
350                ++open_paren_pos; // Skip the open paren
351                const char *close_paren_pos = strchr (open_paren_pos, ')');
352                if (close_paren_pos)
353                    m_category.SetCStringWithLength (open_paren_pos, close_paren_pos - open_paren_pos);
354            }
355        }
356    }
357    return m_category;
358}
359
360ConstString
361ObjCLanguageRuntime::MethodName::GetFullNameWithoutCategory (bool empty_if_no_category)
362{
363    if (IsValid(false))
364    {
365        if (HasCategory())
366        {
367            StreamString strm;
368            if (m_type == eTypeClassMethod)
369                strm.PutChar('+');
370            else if (m_type == eTypeInstanceMethod)
371                strm.PutChar('-');
372            strm.Printf("[%s %s]", GetClassName().GetCString(), GetSelector().GetCString());
373            return ConstString(strm.GetString().c_str());
374        }
375
376        if (!empty_if_no_category)
377        {
378            // Just return the full name since it doesn't have a category
379            return GetFullName();
380        }
381    }
382    return ConstString();
383}
384
385size_t
386ObjCLanguageRuntime::MethodName::GetFullNames (std::vector<ConstString> &names, bool append)
387{
388    if (!append)
389        names.clear();
390    if (IsValid(false))
391    {
392        StreamString strm;
393        const bool is_class_method = m_type == eTypeClassMethod;
394        const bool is_instance_method = m_type == eTypeInstanceMethod;
395        const ConstString &category = GetCategory();
396        if (is_class_method || is_instance_method)
397        {
398            names.push_back (m_full);
399            if (category)
400            {
401                strm.Printf("%c[%s %s]",
402                            is_class_method ? '+' : '-',
403                            GetClassName().GetCString(),
404                            GetSelector().GetCString());
405                names.push_back(ConstString(strm.GetString().c_str()));
406            }
407        }
408        else
409        {
410            const ConstString &class_name = GetClassName();
411            const ConstString &selector = GetSelector();
412            strm.Printf("+[%s %s]", class_name.GetCString(), selector.GetCString());
413            names.push_back(ConstString(strm.GetString().c_str()));
414            strm.Clear();
415            strm.Printf("-[%s %s]", class_name.GetCString(), selector.GetCString());
416            names.push_back(ConstString(strm.GetString().c_str()));
417            strm.Clear();
418            if (category)
419            {
420                strm.Printf("+[%s(%s) %s]", class_name.GetCString(), category.GetCString(), selector.GetCString());
421                names.push_back(ConstString(strm.GetString().c_str()));
422                strm.Clear();
423                strm.Printf("-[%s(%s) %s]", class_name.GetCString(), category.GetCString(), selector.GetCString());
424                names.push_back(ConstString(strm.GetString().c_str()));
425            }
426        }
427    }
428    return names.size();
429}
430
431
432bool
433ObjCLanguageRuntime::ClassDescriptor::IsPointerValid (lldb::addr_t value,
434                                                      uint32_t ptr_size,
435                                                      bool allow_NULLs,
436                                                      bool allow_tagged,
437                                                      bool check_version_specific) const
438{
439    if (!value)
440        return allow_NULLs;
441    if ( (value % 2) == 1  && allow_tagged)
442        return true;
443    if ((value % ptr_size) == 0)
444        return (check_version_specific ? CheckPointer(value,ptr_size) : true);
445    else
446        return false;
447}
448
449ObjCLanguageRuntime::ObjCISA
450ObjCLanguageRuntime::GetISA(const ConstString &name)
451{
452    ISAToDescriptorIterator pos = GetDescriptorIterator (name);
453    if (pos != m_isa_to_descriptor.end())
454        return pos->first;
455    return 0;
456}
457
458ObjCLanguageRuntime::ISAToDescriptorIterator
459ObjCLanguageRuntime::GetDescriptorIterator (const ConstString &name)
460{
461    ISAToDescriptorIterator end = m_isa_to_descriptor.end();
462
463    if (name)
464    {
465        UpdateISAToDescriptorMap();
466        if (m_hash_to_isa_map.empty())
467        {
468            // No name hashes were provided, we need to just linearly power through the
469            // names and find a match
470            for (ISAToDescriptorIterator pos = m_isa_to_descriptor.begin(); pos != end; ++pos)
471            {
472                if (pos->second->GetClassName() == name)
473                    return pos;
474            }
475        }
476        else
477        {
478            // Name hashes were provided, so use them to efficiently lookup name to isa/descriptor
479            const uint32_t name_hash = MappedHash::HashStringUsingDJB (name.GetCString());
480            std::pair <HashToISAIterator, HashToISAIterator> range = m_hash_to_isa_map.equal_range(name_hash);
481            for (HashToISAIterator range_pos = range.first; range_pos != range.second; ++range_pos)
482            {
483                ISAToDescriptorIterator pos = m_isa_to_descriptor.find (range_pos->second);
484                if (pos != m_isa_to_descriptor.end())
485                {
486                    if (pos->second->GetClassName() == name)
487                        return pos;
488                }
489            }
490        }
491    }
492    return end;
493}
494
495
496ObjCLanguageRuntime::ObjCISA
497ObjCLanguageRuntime::GetParentClass(ObjCLanguageRuntime::ObjCISA isa)
498{
499    ClassDescriptorSP objc_class_sp (GetClassDescriptorFromISA(isa));
500    if (objc_class_sp)
501    {
502        ClassDescriptorSP objc_super_class_sp (objc_class_sp->GetSuperclass());
503        if (objc_super_class_sp)
504            return objc_super_class_sp->GetISA();
505    }
506    return 0;
507}
508
509ConstString
510ObjCLanguageRuntime::GetActualTypeName(ObjCLanguageRuntime::ObjCISA isa)
511{
512    ClassDescriptorSP objc_class_sp (GetNonKVOClassDescriptor(isa));
513    if (objc_class_sp)
514        return objc_class_sp->GetClassName();
515    return ConstString();
516}
517
518ObjCLanguageRuntime::ClassDescriptorSP
519ObjCLanguageRuntime::GetClassDescriptorFromClassName (const ConstString &class_name)
520{
521    ISAToDescriptorIterator pos = GetDescriptorIterator (class_name);
522    if (pos != m_isa_to_descriptor.end())
523        return pos->second;
524    return ClassDescriptorSP();
525
526}
527
528ObjCLanguageRuntime::ClassDescriptorSP
529ObjCLanguageRuntime::GetClassDescriptor (ValueObject& valobj)
530{
531    ClassDescriptorSP objc_class_sp;
532    // if we get an invalid VO (which might still happen when playing around
533    // with pointers returned by the expression parser, don't consider this
534    // a valid ObjC object)
535    if (valobj.GetClangType().IsValid())
536    {
537        addr_t isa_pointer = valobj.GetPointerValue();
538        if (isa_pointer != LLDB_INVALID_ADDRESS)
539        {
540            ExecutionContext exe_ctx (valobj.GetExecutionContextRef());
541
542            Process *process = exe_ctx.GetProcessPtr();
543            if (process)
544            {
545                Error error;
546                ObjCISA isa = process->ReadPointerFromMemory(isa_pointer, error);
547                if (isa != LLDB_INVALID_ADDRESS)
548                    objc_class_sp = GetClassDescriptorFromISA (isa);
549            }
550        }
551    }
552    return objc_class_sp;
553}
554
555ObjCLanguageRuntime::ClassDescriptorSP
556ObjCLanguageRuntime::GetNonKVOClassDescriptor (ValueObject& valobj)
557{
558    ObjCLanguageRuntime::ClassDescriptorSP objc_class_sp (GetClassDescriptor (valobj));
559    if (objc_class_sp)
560    {
561        if (!objc_class_sp->IsKVO())
562            return objc_class_sp;
563
564        ClassDescriptorSP non_kvo_objc_class_sp(objc_class_sp->GetSuperclass());
565        if (non_kvo_objc_class_sp && non_kvo_objc_class_sp->IsValid())
566            return non_kvo_objc_class_sp;
567    }
568    return ClassDescriptorSP();
569}
570
571
572ObjCLanguageRuntime::ClassDescriptorSP
573ObjCLanguageRuntime::GetClassDescriptorFromISA (ObjCISA isa)
574{
575    if (isa)
576    {
577        UpdateISAToDescriptorMap();
578        ObjCLanguageRuntime::ISAToDescriptorIterator pos = m_isa_to_descriptor.find(isa);
579        if (pos != m_isa_to_descriptor.end())
580            return pos->second;
581    }
582    return ClassDescriptorSP();
583}
584
585ObjCLanguageRuntime::ClassDescriptorSP
586ObjCLanguageRuntime::GetNonKVOClassDescriptor (ObjCISA isa)
587{
588    if (isa)
589    {
590        ClassDescriptorSP objc_class_sp = GetClassDescriptorFromISA (isa);
591        if (objc_class_sp && objc_class_sp->IsValid())
592        {
593            if (!objc_class_sp->IsKVO())
594                return objc_class_sp;
595
596            ClassDescriptorSP non_kvo_objc_class_sp(objc_class_sp->GetSuperclass());
597            if (non_kvo_objc_class_sp && non_kvo_objc_class_sp->IsValid())
598                return non_kvo_objc_class_sp;
599        }
600    }
601    return ClassDescriptorSP();
602}
603
604
605
606