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