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