1 //===-- AppleObjCTypeEncodingParser.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 9 #include "AppleObjCTypeEncodingParser.h" 10 11 #include "Plugins/ExpressionParser/Clang/ClangUtil.h" 12 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" 13 #include "lldb/Symbol/CompilerType.h" 14 #include "lldb/Target/Process.h" 15 #include "lldb/Target/Target.h" 16 #include "lldb/Utility/StringLexer.h" 17 18 #include "clang/Basic/TargetInfo.h" 19 20 #include <vector> 21 22 using namespace lldb_private; 23 24 AppleObjCTypeEncodingParser::AppleObjCTypeEncodingParser( 25 ObjCLanguageRuntime &runtime) 26 : ObjCLanguageRuntime::EncodingToType(), m_runtime(runtime) { 27 if (!m_scratch_ast_ctx_up) 28 m_scratch_ast_ctx_up = std::make_unique<TypeSystemClang>( 29 "AppleObjCTypeEncodingParser ASTContext", 30 runtime.GetProcess()->GetTarget().GetArchitecture().GetTriple()); 31 } 32 33 std::string AppleObjCTypeEncodingParser::ReadStructName(StringLexer &type) { 34 StreamString buffer; 35 while (type.HasAtLeast(1) && type.Peek() != '=') 36 buffer.Printf("%c", type.Next()); 37 return std::string(buffer.GetString()); 38 } 39 40 std::string AppleObjCTypeEncodingParser::ReadQuotedString(StringLexer &type) { 41 StreamString buffer; 42 while (type.HasAtLeast(1) && type.Peek() != '"') 43 buffer.Printf("%c", type.Next()); 44 StringLexer::Character next = type.Next(); 45 UNUSED_IF_ASSERT_DISABLED(next); 46 assert(next == '"'); 47 return std::string(buffer.GetString()); 48 } 49 50 uint32_t AppleObjCTypeEncodingParser::ReadNumber(StringLexer &type) { 51 uint32_t total = 0; 52 while (type.HasAtLeast(1) && isdigit(type.Peek())) 53 total = 10 * total + (type.Next() - '0'); 54 return total; 55 } 56 57 // as an extension to the published grammar recent runtimes emit structs like 58 // this: 59 // "{CGRect=\"origin\"{CGPoint=\"x\"d\"y\"d}\"size\"{CGSize=\"width\"d\"height\"d}}" 60 61 AppleObjCTypeEncodingParser::StructElement::StructElement() 62 : name(""), type(clang::QualType()), bitfield(0) {} 63 64 AppleObjCTypeEncodingParser::StructElement 65 AppleObjCTypeEncodingParser::ReadStructElement(TypeSystemClang &ast_ctx, 66 StringLexer &type, 67 bool for_expression) { 68 StructElement retval; 69 if (type.NextIf('"')) 70 retval.name = ReadQuotedString(type); 71 if (!type.NextIf('"')) 72 return retval; 73 uint32_t bitfield_size = 0; 74 retval.type = BuildType(ast_ctx, type, for_expression, &bitfield_size); 75 retval.bitfield = bitfield_size; 76 return retval; 77 } 78 79 clang::QualType AppleObjCTypeEncodingParser::BuildStruct( 80 TypeSystemClang &ast_ctx, StringLexer &type, bool for_expression) { 81 return BuildAggregate(ast_ctx, type, for_expression, '{', '}', 82 clang::TTK_Struct); 83 } 84 85 clang::QualType AppleObjCTypeEncodingParser::BuildUnion( 86 TypeSystemClang &ast_ctx, StringLexer &type, bool for_expression) { 87 return BuildAggregate(ast_ctx, type, for_expression, '(', ')', 88 clang::TTK_Union); 89 } 90 91 clang::QualType AppleObjCTypeEncodingParser::BuildAggregate( 92 TypeSystemClang &ast_ctx, StringLexer &type, bool for_expression, 93 char opener, char closer, uint32_t kind) { 94 if (!type.NextIf(opener)) 95 return clang::QualType(); 96 std::string name(ReadStructName(type)); 97 98 // We do not handle templated classes/structs at the moment. If the name has 99 // a < in it, we are going to abandon this. We're still obliged to parse it, 100 // so we just set a flag that means "Don't actually build anything." 101 102 const bool is_templated = name.find('<') != std::string::npos; 103 104 if (!type.NextIf('=')) 105 return clang::QualType(); 106 bool in_union = true; 107 std::vector<StructElement> elements; 108 while (in_union && type.HasAtLeast(1)) { 109 if (type.NextIf(closer)) { 110 in_union = false; 111 break; 112 } else { 113 auto element = ReadStructElement(ast_ctx, type, for_expression); 114 if (element.type.isNull()) 115 break; 116 else 117 elements.push_back(element); 118 } 119 } 120 if (in_union) 121 return clang::QualType(); 122 123 if (is_templated) 124 return clang::QualType(); // This is where we bail out. Sorry! 125 126 CompilerType union_type(ast_ctx.CreateRecordType( 127 nullptr, OptionalClangModuleID(), lldb::eAccessPublic, name, kind, 128 lldb::eLanguageTypeC)); 129 if (union_type) { 130 TypeSystemClang::StartTagDeclarationDefinition(union_type); 131 132 unsigned int count = 0; 133 for (auto element : elements) { 134 if (element.name.empty()) { 135 StreamString elem_name; 136 elem_name.Printf("__unnamed_%u", count); 137 element.name = std::string(elem_name.GetString()); 138 } 139 TypeSystemClang::AddFieldToRecordType( 140 union_type, element.name.c_str(), ast_ctx.GetType(element.type), 141 lldb::eAccessPublic, element.bitfield); 142 ++count; 143 } 144 TypeSystemClang::CompleteTagDeclarationDefinition(union_type); 145 } 146 return ClangUtil::GetQualType(union_type); 147 } 148 149 clang::QualType AppleObjCTypeEncodingParser::BuildArray( 150 TypeSystemClang &ast_ctx, StringLexer &type, bool for_expression) { 151 if (!type.NextIf('[')) 152 return clang::QualType(); 153 uint32_t size = ReadNumber(type); 154 clang::QualType element_type(BuildType(ast_ctx, type, for_expression)); 155 if (!type.NextIf(']')) 156 return clang::QualType(); 157 CompilerType array_type(ast_ctx.CreateArrayType( 158 CompilerType(&ast_ctx, element_type.getAsOpaquePtr()), size, false)); 159 return ClangUtil::GetQualType(array_type); 160 } 161 162 // the runtime can emit these in the form of @"SomeType", giving more specifics 163 // this would be interesting for expression parser interop, but since we 164 // actually try to avoid exposing the ivar info to the expression evaluator, 165 // consume but ignore the type info and always return an 'id'; if anything, 166 // dynamic typing will resolve things for us anyway 167 clang::QualType AppleObjCTypeEncodingParser::BuildObjCObjectPointerType( 168 TypeSystemClang &clang_ast_ctx, StringLexer &type, bool for_expression) { 169 if (!type.NextIf('@')) 170 return clang::QualType(); 171 172 clang::ASTContext &ast_ctx = clang_ast_ctx.getASTContext(); 173 174 std::string name; 175 176 if (type.NextIf('"')) { 177 // We have to be careful here. We're used to seeing 178 // @"NSString" 179 // but in records it is possible that the string following an @ is the name 180 // of the next field and @ means "id". This is the case if anything 181 // unquoted except for "}", the end of the type, or another name follows 182 // the quoted string. 183 // 184 // E.g. 185 // - @"NSString"@ means "id, followed by a field named NSString of type id" 186 // - @"NSString"} means "a pointer to NSString and the end of the struct" - 187 // @"NSString""nextField" means "a pointer to NSString and a field named 188 // nextField" - @"NSString" followed by the end of the string means "a 189 // pointer to NSString" 190 // 191 // As a result, the rule is: If we see @ followed by a quoted string, we 192 // peek. - If we see }, ), ], the end of the string, or a quote ("), the 193 // quoted string is a class name. - If we see anything else, the quoted 194 // string is a field name and we push it back onto type. 195 196 name = ReadQuotedString(type); 197 198 if (type.HasAtLeast(1)) { 199 switch (type.Peek()) { 200 default: 201 // roll back 202 type.PutBack(name.length() + 203 2); // undo our consumption of the string and of the quotes 204 name.clear(); 205 break; 206 case '}': 207 case ')': 208 case ']': 209 case '"': 210 // the quoted string is a class name – see the rule 211 break; 212 } 213 } else { 214 // the quoted string is a class name – see the rule 215 } 216 } 217 218 if (for_expression && !name.empty()) { 219 size_t less_than_pos = name.find('<'); 220 221 if (less_than_pos != std::string::npos) { 222 if (less_than_pos == 0) 223 return ast_ctx.getObjCIdType(); 224 else 225 name.erase(less_than_pos); 226 } 227 228 DeclVendor *decl_vendor = m_runtime.GetDeclVendor(); 229 if (!decl_vendor) 230 return clang::QualType(); 231 232 auto types = decl_vendor->FindTypes(ConstString(name), /*max_matches*/ 1); 233 234 // The user can forward-declare something that has no definition. The runtime 235 // doesn't prohibit this at all. This is a rare and very weird case. We keep 236 // this assert in debug builds so we catch other weird cases. 237 #ifdef LLDB_CONFIGURATION_DEBUG 238 assert(!types.empty()); 239 #else 240 if (types.empty()) 241 return ast_ctx.getObjCIdType(); 242 #endif 243 244 return ClangUtil::GetQualType(types.front().GetPointerType()); 245 } else { 246 // We're going to resolve this dynamically anyway, so just smile and wave. 247 return ast_ctx.getObjCIdType(); 248 } 249 } 250 251 clang::QualType 252 AppleObjCTypeEncodingParser::BuildType(TypeSystemClang &clang_ast_ctx, 253 StringLexer &type, bool for_expression, 254 uint32_t *bitfield_bit_size) { 255 if (!type.HasAtLeast(1)) 256 return clang::QualType(); 257 258 clang::ASTContext &ast_ctx = clang_ast_ctx.getASTContext(); 259 260 switch (type.Peek()) { 261 default: 262 break; 263 case '{': 264 return BuildStruct(clang_ast_ctx, type, for_expression); 265 case '[': 266 return BuildArray(clang_ast_ctx, type, for_expression); 267 case '(': 268 return BuildUnion(clang_ast_ctx, type, for_expression); 269 case '@': 270 return BuildObjCObjectPointerType(clang_ast_ctx, type, for_expression); 271 } 272 273 switch (type.Next()) { 274 default: 275 type.PutBack(1); 276 return clang::QualType(); 277 case 'c': 278 return ast_ctx.CharTy; 279 case 'i': 280 return ast_ctx.IntTy; 281 case 's': 282 return ast_ctx.ShortTy; 283 case 'l': 284 return ast_ctx.getIntTypeForBitwidth(32, true); 285 // this used to be done like this: 286 // return clang_ast_ctx->GetIntTypeFromBitSize(32, true).GetQualType(); 287 // which uses one of the constants if one is available, but we don't think 288 // all this work is necessary. 289 case 'q': 290 return ast_ctx.LongLongTy; 291 case 'C': 292 return ast_ctx.UnsignedCharTy; 293 case 'I': 294 return ast_ctx.UnsignedIntTy; 295 case 'S': 296 return ast_ctx.UnsignedShortTy; 297 case 'L': 298 return ast_ctx.getIntTypeForBitwidth(32, false); 299 // see note for 'l' 300 case 'Q': 301 return ast_ctx.UnsignedLongLongTy; 302 case 'f': 303 return ast_ctx.FloatTy; 304 case 'd': 305 return ast_ctx.DoubleTy; 306 case 'B': 307 return ast_ctx.BoolTy; 308 case 'v': 309 return ast_ctx.VoidTy; 310 case '*': 311 return ast_ctx.getPointerType(ast_ctx.CharTy); 312 case '#': 313 return ast_ctx.getObjCClassType(); 314 case ':': 315 return ast_ctx.getObjCSelType(); 316 case 'b': { 317 uint32_t size = ReadNumber(type); 318 if (bitfield_bit_size) { 319 *bitfield_bit_size = size; 320 return ast_ctx.UnsignedIntTy; // FIXME: the spec is fairly vague here. 321 } else 322 return clang::QualType(); 323 } 324 case 'r': { 325 clang::QualType target_type = 326 BuildType(clang_ast_ctx, type, for_expression); 327 if (target_type.isNull()) 328 return clang::QualType(); 329 else if (target_type == ast_ctx.UnknownAnyTy) 330 return ast_ctx.UnknownAnyTy; 331 else 332 return ast_ctx.getConstType(target_type); 333 } 334 case '^': { 335 if (!for_expression && type.NextIf('?')) { 336 // if we are not supporting the concept of unknownAny, but what is being 337 // created here is an unknownAny*, then we can just get away with a void* 338 // this is theoretically wrong (in the same sense as 'theoretically 339 // nothing exists') but is way better than outright failure in many 340 // practical cases 341 return ast_ctx.VoidPtrTy; 342 } else { 343 clang::QualType target_type = 344 BuildType(clang_ast_ctx, type, for_expression); 345 if (target_type.isNull()) 346 return clang::QualType(); 347 else if (target_type == ast_ctx.UnknownAnyTy) 348 return ast_ctx.UnknownAnyTy; 349 else 350 return ast_ctx.getPointerType(target_type); 351 } 352 } 353 case '?': 354 return for_expression ? ast_ctx.UnknownAnyTy : clang::QualType(); 355 } 356 } 357 358 CompilerType AppleObjCTypeEncodingParser::RealizeType(TypeSystemClang &ast_ctx, 359 const char *name, 360 bool for_expression) { 361 if (name && name[0]) { 362 StringLexer lexer(name); 363 clang::QualType qual_type = BuildType(ast_ctx, lexer, for_expression); 364 return ast_ctx.GetType(qual_type); 365 } 366 return CompilerType(); 367 } 368