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