clang::QualType AppleObjCTypeEncodingParser::BuildAggregate( clang::ASTContext &ast_ctx, lldb_utility::StringLexer &type, bool for_expression, char opener, char closer, uint32_t kind) { if (!type.NextIf(opener)) return clang::QualType(); std::string name(ReadStructName(type)); // We do not handle templated classes/structs at the moment. If the name has // a < in it, we are going to abandon this. We're still obliged to parse it, // so we just set a flag that means "Don't actually build anything." const bool is_templated = name.find('<') != std::string::npos; if (!type.NextIf('=')) return clang::QualType(); bool in_union = true; std::vector<StructElement> elements; while (in_union && type.HasAtLeast(1)) { if (type.NextIf(closer)) { in_union = false; break; } else { auto element = ReadStructElement(ast_ctx, type, for_expression); if (element.type.isNull()) break; else elements.push_back(element); } } if (in_union) return clang::QualType(); if (is_templated) return clang::QualType(); // This is where we bail out. Sorry! ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx); if (!lldb_ctx) return clang::QualType(); CompilerType union_type(lldb_ctx->CreateRecordType( nullptr, lldb::eAccessPublic, name.c_str(), kind, lldb::eLanguageTypeC)); if (union_type) { ClangASTContext::StartTagDeclarationDefinition(union_type); unsigned int count = 0; for (auto element : elements) { if (element.name.empty()) { StreamString elem_name; elem_name.Printf("__unnamed_%u", count); element.name = elem_name.GetString(); } ClangASTContext::AddFieldToRecordType( union_type, element.name.c_str(), CompilerType(&ast_ctx, element.type), lldb::eAccessPublic, element.bitfield); ++count; } ClangASTContext::CompleteTagDeclarationDefinition(union_type); } return ClangUtil::GetQualType(union_type); }
AppleObjCTypeEncodingParser::StructElement AppleObjCTypeEncodingParser::ReadStructElement (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression) { StructElement retval; if (type.NextIf('"')) retval.name = ReadQuotedString(type); if (!type.NextIf('"')) return retval; uint32_t bitfield_size = 0; retval.type = BuildType(ast_ctx, type, for_expression, &bitfield_size); retval.bitfield = bitfield_size; return retval; }
clang::QualType AppleObjCTypeEncodingParser::BuildArray (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression) { if (!type.NextIf('[')) return clang::QualType(); uint32_t size = ReadNumber(type); clang::QualType element_type(BuildType(ast_ctx, type, for_expression)); if (!type.NextIf(']')) return clang::QualType(); ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx); if (!lldb_ctx) return clang::QualType(); ClangASTType array_type(lldb_ctx->CreateArrayType(ClangASTType(&ast_ctx,element_type.getAsOpaquePtr()), size, false)); return array_type.GetQualType(); }
// the runtime can emit these in the form of @"SomeType", giving more specifics // this would be interesting for expression parser interop, but since we actually try // to avoid exposing the ivar info to the expression evaluator, consume but ignore the type info // and always return an 'id'; if anything, dynamic typing will resolve things for us anyway clang::QualType AppleObjCTypeEncodingParser::BuildObjCObjectPointerType (clang::ASTContext &ast_ctx, lldb_utility::StringLexer& type, bool for_expression) { if (!type.NextIf('@')) return clang::QualType(); std::string name; if (type.NextIf('"')) { // We have to be careful here. We're used to seeing // @"NSString" // but in records it is possible that the string following an @ is the name of the next field and @ means "id". // This is the case if anything unquoted except for "}", the end of the type, or another name follows the quoted string. // // E.g. // - @"NSString"@ means "id, followed by a field named NSString of type id" // - @"NSString"} means "a pointer to NSString and the end of the struct" // - @"NSString""nextField" means "a pointer to NSString and a field named nextField" // - @"NSString" followed by the end of the string means "a pointer to NSString" // // As a result, the rule is: If we see @ followed by a quoted string, we peek. // - If we see }, ), ], the end of the string, or a quote ("), the quoted string is a class name. // - If we see anything else, the quoted string is a field name and we push it back onto type. name = ReadQuotedString(type); if (type.HasAtLeast(1)) { switch (type.Peek()) { default: // roll back type.PutBack(name.length() + 2); // undo our consumption of the string and of the quotes name.clear(); break; case '}': case ')': case ']': case '"': // the quoted string is a class name – see the rule break; } } else { // the quoted string is a class name – see the rule } } if (for_expression && !name.empty()) { size_t less_than_pos = name.find_first_of('<'); if (less_than_pos != std::string::npos) { if (less_than_pos == 0) return ast_ctx.getObjCIdType(); else name.erase(less_than_pos); } DeclVendor *decl_vendor = m_runtime.GetDeclVendor(); assert (decl_vendor); // how are we parsing type encodings for expressions if a type vendor isn't in play? const bool append = false; const uint32_t max_matches = 1; std::vector<clang::NamedDecl *> decls; uint32_t num_types = decl_vendor->FindDecls(ConstString(name), append, max_matches, decls); // The user can forward-declare something that has no definition. The runtime doesn't prohibit this at all. // This is a rare and very weird case. We keep this assert in debug builds so we catch other weird cases. #ifdef LLDB_CONFIGURATION_DEBUG assert(num_types); #else if (!num_types) return ast_ctx.getObjCIdType(); #endif return ClangASTContext::GetTypeForDecl(decls[0]).GetPointerType().GetQualType(); } else { // We're going to resolve this dynamically anyway, so just smile and wave. return ast_ctx.getObjCIdType(); } }