1 //===-- AppleObjCSymbolVendor.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 
10 #include "AppleObjCTypeVendor.h"
11 
12 #include "lldb/Core/Log.h"
13 #include "lldb/Core/Module.h"
14 #include "lldb/Expression/ASTDumper.h"
15 #include "lldb/Symbol/ClangExternalASTSourceCommon.h"
16 #include "lldb/Target/ObjCLanguageRuntime.h"
17 #include "lldb/Target/Process.h"
18 #include "lldb/Target/Target.h"
19 
20 #include "clang/AST/ASTContext.h"
21 #include "clang/AST/DeclObjC.h"
22 
23 using namespace lldb_private;
24 
25 class lldb_private::AppleObjCExternalASTSource : public ClangExternalASTSourceCommon
26 {
27 public:
AppleObjCExternalASTSource(AppleObjCTypeVendor & type_vendor)28     AppleObjCExternalASTSource (AppleObjCTypeVendor &type_vendor) :
29         m_type_vendor(type_vendor)
30     {
31     }
32 
33     bool
FindExternalVisibleDeclsByName(const clang::DeclContext * decl_ctx,clang::DeclarationName name)34     FindExternalVisibleDeclsByName (const clang::DeclContext *decl_ctx,
35                                     clang::DeclarationName name)
36     {
37         static unsigned int invocation_id = 0;
38         unsigned int current_id = invocation_id++;
39 
40         Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));  // FIXME - a more appropriate log channel?
41 
42         if (log)
43         {
44             log->Printf("AppleObjCExternalASTSource::FindExternalVisibleDeclsByName[%u] on (ASTContext*)%p Looking for %s in (%sDecl*)%p",
45                         current_id,
46                         &decl_ctx->getParentASTContext(),
47                         name.getAsString().c_str(),
48                         decl_ctx->getDeclKindName(),
49                         decl_ctx);
50         }
51 
52         do
53         {
54             const clang::ObjCInterfaceDecl *interface_decl = llvm::dyn_cast<clang::ObjCInterfaceDecl>(decl_ctx);
55 
56             if (!interface_decl)
57                 break;
58 
59             clang::ObjCInterfaceDecl *non_const_interface_decl = const_cast<clang::ObjCInterfaceDecl*>(interface_decl);
60 
61             if (!m_type_vendor.FinishDecl(non_const_interface_decl))
62                 break;
63 
64             clang::DeclContext::lookup_const_result result = non_const_interface_decl->lookup(name);
65 
66             return (result.size() != 0);
67         }
68         while(0);
69 
70         SetNoExternalVisibleDeclsForName(decl_ctx, name);
71         return false;
72     }
73 
74     clang::ExternalLoadResult
FindExternalLexicalDecls(const clang::DeclContext * DC,bool (* isKindWeWant)(clang::Decl::Kind),llvm::SmallVectorImpl<clang::Decl * > & Decls)75     FindExternalLexicalDecls (const clang::DeclContext *DC,
76                               bool (*isKindWeWant)(clang::Decl::Kind),
77                               llvm::SmallVectorImpl<clang::Decl*> &Decls)
78     {
79         return clang::ELR_Success;
80     }
81 
82     void
CompleteType(clang::TagDecl * tag_decl)83     CompleteType (clang::TagDecl *tag_decl)
84     {
85         static unsigned int invocation_id = 0;
86         unsigned int current_id = invocation_id++;
87 
88         Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));  // FIXME - a more appropriate log channel?
89 
90         if (log)
91         {
92             log->Printf("AppleObjCExternalASTSource::CompleteType[%u] on (ASTContext*)%p Completing (TagDecl*)%p named %s",
93                         current_id,
94                         &tag_decl->getASTContext(),
95                         tag_decl,
96                         tag_decl->getName().str().c_str());
97 
98             log->Printf("  AOEAS::CT[%u] Before:", current_id);
99             ASTDumper dumper((clang::Decl*)tag_decl);
100             dumper.ToLog(log, "    [CT] ");
101         }
102 
103         if (log)
104         {
105             log->Printf("  AOEAS::CT[%u] After:", current_id);
106             ASTDumper dumper((clang::Decl*)tag_decl);
107             dumper.ToLog(log, "    [CT] ");
108         }
109         return;
110     }
111 
112     void
CompleteType(clang::ObjCInterfaceDecl * interface_decl)113     CompleteType (clang::ObjCInterfaceDecl *interface_decl)
114     {
115         static unsigned int invocation_id = 0;
116         unsigned int current_id = invocation_id++;
117 
118         Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));  // FIXME - a more appropriate log channel?
119 
120         if (log)
121         {
122             log->Printf("AppleObjCExternalASTSource::CompleteType[%u] on (ASTContext*)%p Completing (ObjCInterfaceDecl*)%p named %s",
123                         current_id,
124                         &interface_decl->getASTContext(),
125                         interface_decl,
126                         interface_decl->getName().str().c_str());
127 
128             log->Printf("  AOEAS::CT[%u] Before:", current_id);
129             ASTDumper dumper((clang::Decl*)interface_decl);
130             dumper.ToLog(log, "    [CT] ");
131         }
132 
133         m_type_vendor.FinishDecl(interface_decl);
134 
135         if (log)
136         {
137             log->Printf("  [CT] After:");
138             ASTDumper dumper((clang::Decl*)interface_decl);
139             dumper.ToLog(log, "    [CT] ");
140         }
141         return;
142     }
143 
144     bool
layoutRecordType(const clang::RecordDecl * Record,uint64_t & Size,uint64_t & Alignment,llvm::DenseMap<const clang::FieldDecl *,uint64_t> & FieldOffsets,llvm::DenseMap<const clang::CXXRecordDecl *,clang::CharUnits> & BaseOffsets,llvm::DenseMap<const clang::CXXRecordDecl *,clang::CharUnits> & VirtualBaseOffsets)145     layoutRecordType(const clang::RecordDecl *Record,
146                      uint64_t &Size,
147                      uint64_t &Alignment,
148                      llvm::DenseMap <const clang::FieldDecl *, uint64_t> &FieldOffsets,
149                      llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> &BaseOffsets,
150                      llvm::DenseMap <const clang::CXXRecordDecl *, clang::CharUnits> &VirtualBaseOffsets)
151     {
152         return false;
153     }
154 
StartTranslationUnit(clang::ASTConsumer * Consumer)155     void StartTranslationUnit (clang::ASTConsumer *Consumer)
156     {
157         clang::TranslationUnitDecl *translation_unit_decl = m_type_vendor.m_ast_ctx.getASTContext()->getTranslationUnitDecl();
158         translation_unit_decl->setHasExternalVisibleStorage();
159         translation_unit_decl->setHasExternalLexicalStorage();
160     }
161 private:
162     AppleObjCTypeVendor                                    &m_type_vendor;
163 };
164 
AppleObjCTypeVendor(ObjCLanguageRuntime & runtime)165 AppleObjCTypeVendor::AppleObjCTypeVendor(ObjCLanguageRuntime &runtime) :
166     TypeVendor(),
167     m_runtime(runtime),
168     m_ast_ctx(runtime.GetProcess()->GetTarget().GetArchitecture().GetTriple().getTriple().c_str())
169 {
170     m_external_source = new AppleObjCExternalASTSource (*this);
171     llvm::OwningPtr<clang::ExternalASTSource> external_source_owning_ptr (m_external_source);
172     m_ast_ctx.getASTContext()->setExternalSource(external_source_owning_ptr);
173 }
174 
175 clang::ObjCInterfaceDecl*
GetDeclForISA(ObjCLanguageRuntime::ObjCISA isa)176 AppleObjCTypeVendor::GetDeclForISA(ObjCLanguageRuntime::ObjCISA isa)
177 {
178     ISAToInterfaceMap::const_iterator iter = m_isa_to_interface.find(isa);
179 
180     if (iter != m_isa_to_interface.end())
181         return iter->second;
182 
183     clang::ASTContext *ast_ctx = m_ast_ctx.getASTContext();
184 
185     ObjCLanguageRuntime::ClassDescriptorSP descriptor = m_runtime.GetClassDescriptorFromISA(isa);
186 
187     if (!descriptor)
188         return NULL;
189 
190     const ConstString &name(descriptor->GetClassName());
191 
192     clang::IdentifierInfo &identifier_info = ast_ctx->Idents.get(name.GetStringRef());
193 
194     clang::ObjCInterfaceDecl *new_iface_decl = clang::ObjCInterfaceDecl::Create(*ast_ctx,
195                                                                                 ast_ctx->getTranslationUnitDecl(),
196                                                                                 clang::SourceLocation(),
197                                                                                 &identifier_info,
198                                                                                 NULL);
199 
200     ClangASTMetadata meta_data;
201     meta_data.SetISAPtr(isa);
202     m_external_source->SetMetadata(new_iface_decl, meta_data);
203 
204     new_iface_decl->setHasExternalVisibleStorage();
205     new_iface_decl->setHasExternalLexicalStorage();
206 
207     ast_ctx->getTranslationUnitDecl()->addDecl(new_iface_decl);
208 
209     m_isa_to_interface[isa] = new_iface_decl;
210 
211     return new_iface_decl;
212 }
213 
214 class ObjCRuntimeMethodType
215 {
216 public:
ObjCRuntimeMethodType(const char * types)217     ObjCRuntimeMethodType (const char *types) : m_is_valid(false)
218     {
219         const char *cursor = types;
220         enum ParserState {
221             Start = 0,
222             InType,
223             InPos
224         } state = Start;
225         const char *type = NULL;
226         int brace_depth = 0;
227 
228         uint32_t stepsLeft = 256;
229 
230         while (1)
231         {
232             if (--stepsLeft == 0)
233             {
234                 m_is_valid = false;
235                 return;
236             }
237 
238             switch (state)
239             {
240             case Start:
241                 {
242                     switch (*cursor)
243                     {
244                     default:
245                         state = InType;
246                         type = cursor;
247                         break;
248                     case '\0':
249                         m_is_valid = true;
250                         return;
251                     case '0': case '1': case '2': case '3': case '4':
252                     case '5': case '6': case '7': case '8': case '9':
253                         m_is_valid = false;
254                         return;
255                     }
256                 }
257                 break;
258             case InType:
259                 {
260                     switch (*cursor)
261                     {
262                     default:
263                         ++cursor;
264                         break;
265                     case '0': case '1': case '2': case '3': case '4':
266                     case '5': case '6': case '7': case '8': case '9':
267                         if (!brace_depth)
268                         {
269                             state = InPos;
270                             if (type)
271                             {
272                                 m_type_vector.push_back(std::string(type, (cursor - type)));
273                             }
274                             else
275                             {
276                                 m_is_valid = false;
277                                 return;
278                             }
279                             type = NULL;
280                         }
281                         else
282                         {
283                             ++cursor;
284                         }
285                         break;
286                     case '[': case '{': case '(':
287                         ++brace_depth;
288                         ++cursor;
289                         break;
290                     case ']': case '}': case ')':
291                         if (!brace_depth)
292                         {
293                             m_is_valid = false;
294                             return;
295                         }
296                         --brace_depth;
297                         ++cursor;
298                         break;
299                     case '\0':
300                         m_is_valid = false;
301                         return;
302                     }
303                 }
304                 break;
305             case InPos:
306                 {
307                     switch (*cursor)
308                     {
309                     default:
310                         state = InType;
311                         type = cursor;
312                         break;
313                     case '0': case '1': case '2': case '3': case '4':
314                     case '5': case '6': case '7': case '8': case '9':
315                         ++cursor;
316                         break;
317                     case '\0':
318                         m_is_valid = true;
319                         return;
320                     }
321                 }
322                 break;
323             }
324         }
325     }
326 
BuildMethod(clang::ObjCInterfaceDecl * interface_decl,const char * name,bool instance)327     clang::ObjCMethodDecl *BuildMethod (clang::ObjCInterfaceDecl *interface_decl, const char *name, bool instance)
328     {
329         if (!m_is_valid || m_type_vector.size() < 3)
330             return NULL;
331 
332         clang::ASTContext &ast_ctx(interface_decl->getASTContext());
333 
334         clang::QualType return_qual_type;
335 
336         const bool isInstance = instance;
337         const bool isVariadic = false;
338         const bool isSynthesized = false;
339         const bool isImplicitlyDeclared = true;
340         const bool isDefined = false;
341         const clang::ObjCMethodDecl::ImplementationControl impControl = clang::ObjCMethodDecl::None;
342         const bool HasRelatedResultType = false;
343 
344         std::vector <clang::IdentifierInfo *> selector_components;
345 
346         const char *name_cursor = name;
347         bool is_zero_argument = true;
348 
349         while (*name_cursor != '\0')
350         {
351             const char *colon_loc = strchr(name_cursor, ':');
352             if (!colon_loc)
353             {
354                 selector_components.push_back(&ast_ctx.Idents.get(llvm::StringRef(name_cursor)));
355                 break;
356             }
357             else
358             {
359                 is_zero_argument = false;
360                 selector_components.push_back(&ast_ctx.Idents.get(llvm::StringRef(name_cursor, colon_loc - name_cursor)));
361                 name_cursor = colon_loc + 1;
362             }
363         }
364 
365         clang::Selector sel = ast_ctx.Selectors.getSelector(is_zero_argument ? 0 : selector_components.size(), selector_components.data());
366 
367         clang::QualType ret_type = BuildType(ast_ctx, m_type_vector[0].c_str());
368 
369         if (ret_type.isNull())
370             return NULL;
371 
372         clang::ObjCMethodDecl *ret = clang::ObjCMethodDecl::Create(ast_ctx,
373                                                                    clang::SourceLocation(),
374                                                                    clang::SourceLocation(),
375                                                                    sel,
376                                                                    ret_type,
377                                                                    NULL,
378                                                                    interface_decl,
379                                                                    isInstance,
380                                                                    isVariadic,
381                                                                    isSynthesized,
382                                                                    isImplicitlyDeclared,
383                                                                    isDefined,
384                                                                    impControl,
385                                                                    HasRelatedResultType);
386 
387         std::vector <clang::ParmVarDecl*> parm_vars;
388 
389         for (size_t ai = 3, ae = m_type_vector.size();
390              ai != ae;
391              ++ai)
392         {
393             clang::QualType arg_type = BuildType(ast_ctx, m_type_vector[ai].c_str());
394 
395             if (arg_type.isNull())
396                 return NULL; // well, we just wasted a bunch of time.  Wish we could delete the stuff we'd just made!
397 
398             parm_vars.push_back(clang::ParmVarDecl::Create(ast_ctx,
399                                                            ret,
400                                                            clang::SourceLocation(),
401                                                            clang::SourceLocation(),
402                                                            NULL,
403                                                            arg_type,
404                                                            NULL,
405                                                            clang::SC_None,
406                                                            NULL));
407         }
408 
409         ret->setMethodParams(ast_ctx, llvm::ArrayRef<clang::ParmVarDecl*>(parm_vars), llvm::ArrayRef<clang::SourceLocation>());
410 
411         return ret;
412     }
413 private:
BuildType(clang::ASTContext & ast_ctx,const char * type)414     clang::QualType BuildType (clang::ASTContext &ast_ctx, const char *type)
415     {
416         if (!type)
417             return clang::QualType();
418 
419         switch (*type)
420         {
421         default:
422             return ast_ctx.UnknownAnyTy;
423         case 'r':
424             {
425                 clang::QualType target_type = BuildType(ast_ctx, type+1);
426                 if (target_type.isNull())
427                     return clang::QualType();
428                 else if (target_type == ast_ctx.UnknownAnyTy)
429                     return ast_ctx.UnknownAnyTy;
430                 else
431                     return ast_ctx.getConstType(target_type);
432             }
433         case '^':
434         {
435             clang::QualType target_type = BuildType(ast_ctx, type+1);
436             if (target_type.isNull())
437                 return clang::QualType();
438             else if (target_type == ast_ctx.UnknownAnyTy)
439                 return ast_ctx.UnknownAnyTy;
440             else
441                 return ast_ctx.getPointerType(target_type);
442         }
443         case 'c':
444             return ast_ctx.CharTy;
445         case 'i':
446             return ast_ctx.IntTy;
447         case 's':
448             return ast_ctx.ShortTy;
449         case 'l':
450             if (ast_ctx.getTypeSize(ast_ctx.VoidTy) == 64)
451                 return ast_ctx.IntTy;
452             else
453                 return ast_ctx.LongTy;
454         case 'q':
455             return ast_ctx.LongLongTy;
456         case 'C':
457             return ast_ctx.UnsignedCharTy;
458         case 'I':
459             return ast_ctx.UnsignedIntTy;
460         case 'S':
461             return ast_ctx.UnsignedShortTy;
462         case 'L':
463             if (ast_ctx.getTypeSize(ast_ctx.VoidTy) == 64)
464                 return ast_ctx.UnsignedIntTy;
465             else
466                 return ast_ctx.UnsignedLongTy;
467         case 'Q':
468             return ast_ctx.UnsignedLongLongTy;
469         case 'f':
470             return ast_ctx.FloatTy;
471         case 'd':
472             return ast_ctx.DoubleTy;
473         case 'B':
474             return ast_ctx.BoolTy;
475         case 'v':
476             return ast_ctx.VoidTy;
477         case '*':
478             return ast_ctx.getPointerType(ast_ctx.CharTy);
479         case '@':
480             return ast_ctx.getObjCIdType();
481         case '#':
482             return ast_ctx.getObjCClassType();
483         case ':':
484             return ast_ctx.getObjCSelType();
485         }
486         return clang::QualType();
487     }
488 
489     typedef std::vector <std::string> TypeVector;
490 
491     TypeVector  m_type_vector;
492     bool        m_is_valid;
493 };
494 
495 bool
FinishDecl(clang::ObjCInterfaceDecl * interface_decl)496 AppleObjCTypeVendor::FinishDecl(clang::ObjCInterfaceDecl *interface_decl)
497 {
498     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));  // FIXME - a more appropriate log channel?
499 
500     ClangASTMetadata *metadata = m_external_source->GetMetadata(interface_decl);
501     ObjCLanguageRuntime::ObjCISA objc_isa = 0;
502     if (metadata)
503      objc_isa = metadata->GetISAPtr();
504 
505     if (!objc_isa)
506         return false;
507 
508     if (!interface_decl->hasExternalVisibleStorage())
509         return true;
510 
511     interface_decl->startDefinition();
512 
513     interface_decl->setHasExternalVisibleStorage(false);
514     interface_decl->setHasExternalLexicalStorage(false);
515 
516     ObjCLanguageRuntime::ClassDescriptorSP descriptor = m_runtime.GetClassDescriptorFromISA(objc_isa);
517 
518     if (!descriptor)
519         return false;
520 
521     auto superclass_func = [interface_decl, this](ObjCLanguageRuntime::ObjCISA isa)
522     {
523         clang::ObjCInterfaceDecl *superclass_decl = GetDeclForISA(isa);
524         if (!superclass_decl)
525             return;
526         interface_decl->setSuperClass(superclass_decl);
527     };
528 
529     auto instance_method_func = [log, interface_decl, this](const char *name, const char *types) -> bool
530     {
531         ObjCRuntimeMethodType method_type(types);
532 
533         clang::ObjCMethodDecl *method_decl = method_type.BuildMethod (interface_decl, name, true);
534 
535         if (log)
536             log->Printf("[  AOTV::FD] Instance method [%s] [%s]", name, types);
537 
538         if (method_decl)
539             interface_decl->addDecl(method_decl);
540 
541         return false;
542     };
543 
544     auto class_method_func = [log, interface_decl, this](const char *name, const char *types) -> bool
545     {
546         ObjCRuntimeMethodType method_type(types);
547 
548         clang::ObjCMethodDecl *method_decl = method_type.BuildMethod (interface_decl, name, false);
549 
550         if (log)
551             log->Printf("[  AOTV::FD] Class method [%s] [%s]", name, types);
552 
553         if (method_decl)
554             interface_decl->addDecl(method_decl);
555 
556         return false;
557     };
558 
559     if (log)
560     {
561         ASTDumper method_dumper ((clang::Decl*)interface_decl);
562 
563         log->Printf("[AppleObjCTypeVendor::FinishDecl] Finishing Objective-C interface for %s", descriptor->GetClassName().AsCString());
564     }
565 
566 
567     if (!descriptor->Describe(superclass_func,
568                               instance_method_func,
569                               class_method_func,
570                               std::function <bool (const char *, const char *, lldb::addr_t, uint64_t)> (nullptr)))
571         return false;
572 
573     if (log)
574     {
575         ASTDumper method_dumper ((clang::Decl*)interface_decl);
576 
577         log->Printf("[AppleObjCTypeVendor::FinishDecl] Finished Objective-C interface");
578 
579         method_dumper.ToLog(log, "  [AOTV::FD] ");
580     }
581 
582     return true;
583 }
584 
585 uint32_t
FindTypes(const ConstString & name,bool append,uint32_t max_matches,std::vector<ClangASTType> & types)586 AppleObjCTypeVendor::FindTypes (const ConstString &name,
587                                 bool append,
588                                 uint32_t max_matches,
589                                 std::vector <ClangASTType> &types)
590 {
591     static unsigned int invocation_id = 0;
592     unsigned int current_id = invocation_id++;
593 
594     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));  // FIXME - a more appropriate log channel?
595 
596     if (log)
597         log->Printf("AppleObjCTypeVendor::FindTypes [%u] ('%s', %s, %u, )",
598                     current_id,
599                     (const char*)name.AsCString(),
600                     append ? "true" : "false",
601                     max_matches);
602 
603     if (!append)
604         types.clear();
605 
606     uint32_t ret = 0;
607 
608     do
609     {
610         // See if the type is already in our ASTContext.
611 
612         clang::ASTContext *ast_ctx = m_ast_ctx.getASTContext();
613 
614         clang::IdentifierInfo &identifier_info = ast_ctx->Idents.get(name.GetStringRef());
615         clang::DeclarationName decl_name = ast_ctx->DeclarationNames.getIdentifier(&identifier_info);
616 
617         clang::DeclContext::lookup_const_result lookup_result = ast_ctx->getTranslationUnitDecl()->lookup(decl_name);
618 
619         if (!lookup_result.empty())
620         {
621             if (const clang::ObjCInterfaceDecl *result_iface_decl = llvm::dyn_cast<clang::ObjCInterfaceDecl>(lookup_result[0]))
622             {
623                 clang::QualType result_iface_type = ast_ctx->getObjCInterfaceType(result_iface_decl);
624 
625                 if (log)
626                 {
627                     ASTDumper dumper(result_iface_type);
628 
629                     uint64_t isa_value = LLDB_INVALID_ADDRESS;
630                     ClangASTMetadata *metadata = m_external_source->GetMetadata(result_iface_decl);
631                     if (metadata)
632                         isa_value = metadata->GetISAPtr();
633 
634                     log->Printf("AOCTV::FT [%u] Found %s (isa 0x%" PRIx64 ") in the ASTContext",
635                                 current_id,
636                                 dumper.GetCString(),
637                                 isa_value);
638                 }
639 
640                 types.push_back(ClangASTType(ast_ctx, result_iface_type.getAsOpaquePtr()));
641                 ret++;
642                 break;
643             }
644             else
645             {
646                 if (log)
647                     log->Printf("AOCTV::FT [%u] There's something in the ASTContext, but it's not something we know about",
648                                 current_id);
649                 break;
650             }
651         }
652         else if(log)
653         {
654             log->Printf("AOCTV::FT [%u] Couldn't find %s in the ASTContext",
655                         current_id,
656                         name.AsCString());
657         }
658 
659         // It's not.  If it exists, we have to put it into our ASTContext.
660 
661         ObjCLanguageRuntime::ObjCISA isa = m_runtime.GetISA(name);
662 
663         if (!isa)
664         {
665             if (log)
666                 log->Printf("AOCTV::FT [%u] Couldn't find the isa",
667                             current_id);
668 
669             break;
670         }
671 
672         clang::ObjCInterfaceDecl *iface_decl = GetDeclForISA(isa);
673 
674         if (!iface_decl)
675         {
676             if (log)
677                 log->Printf("AOCTV::FT [%u] Couldn't get the Objective-C interface for isa 0x%" PRIx64,
678                             current_id,
679                             (uint64_t)isa);
680 
681             break;
682         }
683 
684         clang::QualType new_iface_type = ast_ctx->getObjCInterfaceType(iface_decl);
685 
686         if (log)
687         {
688             ASTDumper dumper(new_iface_type);
689             log->Printf("AOCTV::FT [%u] Created %s (isa 0x%" PRIx64 ")",
690                         current_id,
691                         dumper.GetCString(),
692                         (uint64_t)isa);
693         }
694 
695         types.push_back(ClangASTType(ast_ctx, new_iface_type.getAsOpaquePtr()));
696         ret++;
697         break;
698     } while (0);
699 
700     return ret;
701 }
702