uint32_t AppleObjCTypeEncodingParser::ReadNumber(lldb_utility::StringLexer &type) { uint32_t total = 0; while (type.HasAtLeast(1) && isdigit(type.Peek())) total = 10 * total + (type.Next() - '0'); return total; }
std::string AppleObjCTypeEncodingParser::ReadStructName(lldb_utility::StringLexer &type) { StreamString buffer; while (type.HasAtLeast(1) && type.Peek() != '=') buffer.Printf("%c", type.Next()); return buffer.GetString(); }
std::string AppleObjCTypeEncodingParser::ReadQuotedString(lldb_utility::StringLexer& type) { StreamString buffer; while (type.HasAtLeast(1) && type.Peek() != '"') buffer.Printf("%c",type.Next()); StringLexer::Character next = type.Next(); assert (next == '"'); return buffer.GetString(); }
// 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(); } }