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