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_sp) 28 return; 29 30 m_scratch_ast_ctx_sp = std::make_shared<TypeSystemClang>( 31 "AppleObjCTypeEncodingParser ASTContext", 32 runtime.GetProcess()->GetTarget().GetArchitecture().GetTriple()); 33 } 34 35 std::string AppleObjCTypeEncodingParser::ReadStructName(StringLexer &type) { 36 StreamString buffer; 37 while (type.HasAtLeast(1) && type.Peek() != '=') 38 buffer.Printf("%c", type.Next()); 39 return std::string(buffer.GetString()); 40 } 41 42 std::string AppleObjCTypeEncodingParser::ReadQuotedString(StringLexer &type) { 43 StreamString buffer; 44 while (type.HasAtLeast(1) && type.Peek() != '"') 45 buffer.Printf("%c", type.Next()); 46 StringLexer::Character next = type.Next(); 47 UNUSED_IF_ASSERT_DISABLED(next); 48 assert(next == '"'); 49 return std::string(buffer.GetString()); 50 } 51 52 uint32_t AppleObjCTypeEncodingParser::ReadNumber(StringLexer &type) { 53 uint32_t total = 0; 54 while (type.HasAtLeast(1) && isdigit(type.Peek())) 55 total = 10 * total + (type.Next() - '0'); 56 return total; 57 } 58 59 // as an extension to the published grammar recent runtimes emit structs like 60 // this: 61 // "{CGRect=\"origin\"{CGPoint=\"x\"d\"y\"d}\"size\"{CGSize=\"width\"d\"height\"d}}" 62 63 AppleObjCTypeEncodingParser::StructElement::StructElement() 64 : type(clang::QualType()) {} 65 66 AppleObjCTypeEncodingParser::StructElement 67 AppleObjCTypeEncodingParser::ReadStructElement(TypeSystemClang &ast_ctx, 68 StringLexer &type, 69 bool for_expression) { 70 StructElement retval; 71 if (type.NextIf('"')) 72 retval.name = ReadQuotedString(type); 73 if (!type.NextIf('"')) 74 return retval; 75 uint32_t bitfield_size = 0; 76 retval.type = BuildType(ast_ctx, type, for_expression, &bitfield_size); 77 retval.bitfield = bitfield_size; 78 return retval; 79 } 80 81 clang::QualType AppleObjCTypeEncodingParser::BuildStruct( 82 TypeSystemClang &ast_ctx, StringLexer &type, bool for_expression) { 83 return BuildAggregate(ast_ctx, type, for_expression, _C_STRUCT_B, _C_STRUCT_E, 84 clang::TTK_Struct); 85 } 86 87 clang::QualType AppleObjCTypeEncodingParser::BuildUnion( 88 TypeSystemClang &ast_ctx, StringLexer &type, bool for_expression) { 89 return BuildAggregate(ast_ctx, type, for_expression, _C_UNION_B, _C_UNION_E, 90 clang::TTK_Union); 91 } 92 93 clang::QualType AppleObjCTypeEncodingParser::BuildAggregate( 94 TypeSystemClang &ast_ctx, StringLexer &type, bool for_expression, 95 char opener, char closer, uint32_t kind) { 96 if (!type.NextIf(opener)) 97 return clang::QualType(); 98 std::string name(ReadStructName(type)); 99 100 // We do not handle templated classes/structs at the moment. If the name has 101 // a < in it, we are going to abandon this. We're still obliged to parse it, 102 // so we just set a flag that means "Don't actually build anything." 103 104 const bool is_templated = name.find('<') != std::string::npos; 105 106 if (!type.NextIf('=')) 107 return clang::QualType(); 108 bool in_union = true; 109 std::vector<StructElement> elements; 110 while (in_union && type.HasAtLeast(1)) { 111 if (type.NextIf(closer)) { 112 in_union = false; 113 break; 114 } else { 115 auto element = ReadStructElement(ast_ctx, type, for_expression); 116 if (element.type.isNull()) 117 break; 118 else 119 elements.push_back(element); 120 } 121 } 122 if (in_union) 123 return clang::QualType(); 124 125 if (is_templated) 126 return clang::QualType(); // This is where we bail out. Sorry! 127 128 CompilerType union_type(ast_ctx.CreateRecordType( 129 nullptr, OptionalClangModuleID(), lldb::eAccessPublic, name, kind, 130 lldb::eLanguageTypeC)); 131 if (union_type) { 132 TypeSystemClang::StartTagDeclarationDefinition(union_type); 133 134 unsigned int count = 0; 135 for (auto element : elements) { 136 if (element.name.empty()) { 137 StreamString elem_name; 138 elem_name.Printf("__unnamed_%u", count); 139 element.name = std::string(elem_name.GetString()); 140 } 141 TypeSystemClang::AddFieldToRecordType( 142 union_type, element.name.c_str(), ast_ctx.GetType(element.type), 143 lldb::eAccessPublic, element.bitfield); 144 ++count; 145 } 146 TypeSystemClang::CompleteTagDeclarationDefinition(union_type); 147 } 148 return ClangUtil::GetQualType(union_type); 149 } 150 151 clang::QualType AppleObjCTypeEncodingParser::BuildArray( 152 TypeSystemClang &ast_ctx, StringLexer &type, bool for_expression) { 153 if (!type.NextIf(_C_ARY_B)) 154 return clang::QualType(); 155 uint32_t size = ReadNumber(type); 156 clang::QualType element_type(BuildType(ast_ctx, type, for_expression)); 157 if (!type.NextIf(_C_ARY_E)) 158 return clang::QualType(); 159 CompilerType array_type(ast_ctx.CreateArrayType( 160 CompilerType(ast_ctx.weak_from_this(), element_type.getAsOpaquePtr()), 161 size, false)); 162 return ClangUtil::GetQualType(array_type); 163 } 164 165 // the runtime can emit these in the form of @"SomeType", giving more specifics 166 // this would be interesting for expression parser interop, but since we 167 // actually try to avoid exposing the ivar info to the expression evaluator, 168 // consume but ignore the type info and always return an 'id'; if anything, 169 // dynamic typing will resolve things for us anyway 170 clang::QualType AppleObjCTypeEncodingParser::BuildObjCObjectPointerType( 171 TypeSystemClang &clang_ast_ctx, StringLexer &type, bool for_expression) { 172 if (!type.NextIf(_C_ID)) 173 return clang::QualType(); 174 175 clang::ASTContext &ast_ctx = clang_ast_ctx.getASTContext(); 176 177 std::string name; 178 179 if (type.NextIf('"')) { 180 // We have to be careful here. We're used to seeing 181 // @"NSString" 182 // but in records it is possible that the string following an @ is the name 183 // of the next field and @ means "id". This is the case if anything 184 // unquoted except for "}", the end of the type, or another name follows 185 // the quoted string. 186 // 187 // E.g. 188 // - @"NSString"@ means "id, followed by a field named NSString of type id" 189 // - @"NSString"} means "a pointer to NSString and the end of the struct" - 190 // @"NSString""nextField" means "a pointer to NSString and a field named 191 // nextField" - @"NSString" followed by the end of the string means "a 192 // pointer to NSString" 193 // 194 // As a result, the rule is: If we see @ followed by a quoted string, we 195 // peek. - If we see }, ), ], the end of the string, or a quote ("), the 196 // quoted string is a class name. - If we see anything else, the quoted 197 // string is a field name and we push it back onto type. 198 199 name = ReadQuotedString(type); 200 201 if (type.HasAtLeast(1)) { 202 switch (type.Peek()) { 203 default: 204 // roll back 205 type.PutBack(name.length() + 206 2); // undo our consumption of the string and of the quotes 207 name.clear(); 208 break; 209 case _C_STRUCT_E: 210 case _C_UNION_E: 211 case _C_ARY_E: 212 case '"': 213 // the quoted string is a class name – see the rule 214 break; 215 } 216 } else { 217 // the quoted string is a class name – see the rule 218 } 219 } 220 221 if (for_expression && !name.empty()) { 222 size_t less_than_pos = name.find('<'); 223 224 if (less_than_pos != std::string::npos) { 225 if (less_than_pos == 0) 226 return ast_ctx.getObjCIdType(); 227 else 228 name.erase(less_than_pos); 229 } 230 231 DeclVendor *decl_vendor = m_runtime.GetDeclVendor(); 232 if (!decl_vendor) 233 return clang::QualType(); 234 235 auto types = decl_vendor->FindTypes(ConstString(name), /*max_matches*/ 1); 236 237 // The user can forward-declare something that has no definition. The runtime 238 // doesn't prohibit this at all. This is a rare and very weird case. We keep 239 // this assert in debug builds so we catch other weird cases. 240 lldbassert(!types.empty()); 241 if (types.empty()) 242 return ast_ctx.getObjCIdType(); 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 _C_STRUCT_B: 264 return BuildStruct(clang_ast_ctx, type, for_expression); 265 case _C_ARY_B: 266 return BuildArray(clang_ast_ctx, type, for_expression); 267 case _C_UNION_B: 268 return BuildUnion(clang_ast_ctx, type, for_expression); 269 case _C_ID: 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_CHR: 278 return ast_ctx.CharTy; 279 case _C_INT: 280 return ast_ctx.IntTy; 281 case _C_SHT: 282 return ast_ctx.ShortTy; 283 case _C_LNG: 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 _C_LNG_LNG: 290 return ast_ctx.LongLongTy; 291 case _C_UCHR: 292 return ast_ctx.UnsignedCharTy; 293 case _C_UINT: 294 return ast_ctx.UnsignedIntTy; 295 case _C_USHT: 296 return ast_ctx.UnsignedShortTy; 297 case _C_ULNG: 298 return ast_ctx.getIntTypeForBitwidth(32, false); 299 // see note for _C_LNG 300 case _C_ULNG_LNG: 301 return ast_ctx.UnsignedLongLongTy; 302 case _C_FLT: 303 return ast_ctx.FloatTy; 304 case _C_DBL: 305 return ast_ctx.DoubleTy; 306 case _C_BOOL: 307 return ast_ctx.BoolTy; 308 case _C_VOID: 309 return ast_ctx.VoidTy; 310 case _C_CHARPTR: 311 return ast_ctx.getPointerType(ast_ctx.CharTy); 312 case _C_CLASS: 313 return ast_ctx.getObjCClassType(); 314 case _C_SEL: 315 return ast_ctx.getObjCSelType(); 316 case _C_BFLD: { 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 _C_CONST: { 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 _C_PTR: { 335 if (!for_expression && type.NextIf(_C_UNDEF)) { 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 _C_UNDEF: 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