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 
AppleObjCTypeEncodingParser(ObjCLanguageRuntime & runtime)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 
ReadStructName(StringLexer & type)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 
ReadQuotedString(StringLexer & type)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 
ReadNumber(StringLexer & type)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 
StructElement()65 AppleObjCTypeEncodingParser::StructElement::StructElement()
66     : type(clang::QualType()) {}
67 
68 AppleObjCTypeEncodingParser::StructElement
ReadStructElement(TypeSystemClang & ast_ctx,StringLexer & type,bool for_expression)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 
BuildStruct(TypeSystemClang & ast_ctx,StringLexer & type,bool for_expression)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 
BuildUnion(TypeSystemClang & ast_ctx,StringLexer & type,bool for_expression)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 
BuildAggregate(TypeSystemClang & ast_ctx,StringLexer & type,bool for_expression,char opener,char closer,uint32_t kind)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 
BuildArray(TypeSystemClang & ast_ctx,StringLexer & type,bool for_expression)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
BuildObjCObjectPointerType(TypeSystemClang & clang_ast_ctx,StringLexer & type,bool for_expression)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
BuildType(TypeSystemClang & clang_ast_ctx,StringLexer & type,bool for_expression,uint32_t * bitfield_bit_size)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 
RealizeType(TypeSystemClang & ast_ctx,const char * name,bool for_expression)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