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 
AppleObjCTypeEncodingParser(ObjCLanguageRuntime & runtime)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 
ReadStructName(StringLexer & type)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 
ReadQuotedString(StringLexer & type)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 
ReadNumber(StringLexer & type)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 
StructElement()63 AppleObjCTypeEncodingParser::StructElement::StructElement()
64     : type(clang::QualType()) {}
65 
66 AppleObjCTypeEncodingParser::StructElement
ReadStructElement(TypeSystemClang & ast_ctx,StringLexer & type,bool for_expression)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 
BuildStruct(TypeSystemClang & ast_ctx,StringLexer & type,bool for_expression)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 
BuildUnion(TypeSystemClang & ast_ctx,StringLexer & type,bool for_expression)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 
BuildAggregate(TypeSystemClang & ast_ctx,StringLexer & type,bool for_expression,char opener,char closer,uint32_t kind)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 
BuildArray(TypeSystemClang & ast_ctx,StringLexer & type,bool for_expression)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
BuildObjCObjectPointerType(TypeSystemClang & clang_ast_ctx,StringLexer & type,bool for_expression)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 #ifdef LLDB_CONFIGURATION_DEBUG
241     assert(!types.empty());
242 #else
243     if (types.empty())
244       return ast_ctx.getObjCIdType();
245 #endif
246 
247     return ClangUtil::GetQualType(types.front().GetPointerType());
248   } else {
249     // We're going to resolve this dynamically anyway, so just smile and wave.
250     return ast_ctx.getObjCIdType();
251   }
252 }
253 
254 clang::QualType
BuildType(TypeSystemClang & clang_ast_ctx,StringLexer & type,bool for_expression,uint32_t * bitfield_bit_size)255 AppleObjCTypeEncodingParser::BuildType(TypeSystemClang &clang_ast_ctx,
256                                        StringLexer &type, bool for_expression,
257                                        uint32_t *bitfield_bit_size) {
258   if (!type.HasAtLeast(1))
259     return clang::QualType();
260 
261   clang::ASTContext &ast_ctx = clang_ast_ctx.getASTContext();
262 
263   switch (type.Peek()) {
264   default:
265     break;
266   case _C_STRUCT_B:
267     return BuildStruct(clang_ast_ctx, type, for_expression);
268   case _C_ARY_B:
269     return BuildArray(clang_ast_ctx, type, for_expression);
270   case _C_UNION_B:
271     return BuildUnion(clang_ast_ctx, type, for_expression);
272   case _C_ID:
273     return BuildObjCObjectPointerType(clang_ast_ctx, type, for_expression);
274   }
275 
276   switch (type.Next()) {
277   default:
278     type.PutBack(1);
279     return clang::QualType();
280   case _C_CHR:
281     return ast_ctx.CharTy;
282   case _C_INT:
283     return ast_ctx.IntTy;
284   case _C_SHT:
285     return ast_ctx.ShortTy;
286   case _C_LNG:
287     return ast_ctx.getIntTypeForBitwidth(32, true);
288   // this used to be done like this:
289   //   return clang_ast_ctx->GetIntTypeFromBitSize(32, true).GetQualType();
290   // which uses one of the constants if one is available, but we don't think
291   // all this work is necessary.
292   case _C_LNG_LNG:
293     return ast_ctx.LongLongTy;
294   case _C_UCHR:
295     return ast_ctx.UnsignedCharTy;
296   case _C_UINT:
297     return ast_ctx.UnsignedIntTy;
298   case _C_USHT:
299     return ast_ctx.UnsignedShortTy;
300   case _C_ULNG:
301     return ast_ctx.getIntTypeForBitwidth(32, false);
302   // see note for _C_LNG
303   case _C_ULNG_LNG:
304     return ast_ctx.UnsignedLongLongTy;
305   case _C_FLT:
306     return ast_ctx.FloatTy;
307   case _C_DBL:
308     return ast_ctx.DoubleTy;
309   case _C_BOOL:
310     return ast_ctx.BoolTy;
311   case _C_VOID:
312     return ast_ctx.VoidTy;
313   case _C_CHARPTR:
314     return ast_ctx.getPointerType(ast_ctx.CharTy);
315   case _C_CLASS:
316     return ast_ctx.getObjCClassType();
317   case _C_SEL:
318     return ast_ctx.getObjCSelType();
319   case _C_BFLD: {
320     uint32_t size = ReadNumber(type);
321     if (bitfield_bit_size) {
322       *bitfield_bit_size = size;
323       return ast_ctx.UnsignedIntTy; // FIXME: the spec is fairly vague here.
324     } else
325       return clang::QualType();
326   }
327   case _C_CONST: {
328     clang::QualType target_type =
329         BuildType(clang_ast_ctx, type, for_expression);
330     if (target_type.isNull())
331       return clang::QualType();
332     else if (target_type == ast_ctx.UnknownAnyTy)
333       return ast_ctx.UnknownAnyTy;
334     else
335       return ast_ctx.getConstType(target_type);
336   }
337   case _C_PTR: {
338     if (!for_expression && type.NextIf(_C_UNDEF)) {
339       // if we are not supporting the concept of unknownAny, but what is being
340       // created here is an unknownAny*, then we can just get away with a void*
341       // this is theoretically wrong (in the same sense as 'theoretically
342       // nothing exists') but is way better than outright failure in many
343       // practical cases
344       return ast_ctx.VoidPtrTy;
345     } else {
346       clang::QualType target_type =
347           BuildType(clang_ast_ctx, type, for_expression);
348       if (target_type.isNull())
349         return clang::QualType();
350       else if (target_type == ast_ctx.UnknownAnyTy)
351         return ast_ctx.UnknownAnyTy;
352       else
353         return ast_ctx.getPointerType(target_type);
354     }
355   }
356   case _C_UNDEF:
357     return for_expression ? ast_ctx.UnknownAnyTy : clang::QualType();
358   }
359 }
360 
RealizeType(TypeSystemClang & ast_ctx,const char * name,bool for_expression)361 CompilerType AppleObjCTypeEncodingParser::RealizeType(TypeSystemClang &ast_ctx,
362                                                       const char *name,
363                                                       bool for_expression) {
364   if (name && name[0]) {
365     StringLexer lexer(name);
366     clang::QualType qual_type = BuildType(ast_ctx, lexer, for_expression);
367     return ast_ctx.GetType(qual_type);
368   }
369   return CompilerType();
370 }
371