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
AppleObjCTypeEncodingParser(ObjCLanguageRuntime & runtime)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
ReadStructName(lldb_utility::StringLexer & type)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
ReadQuotedString(lldb_utility::StringLexer & type)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
ReadNumber(lldb_utility::StringLexer & type)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
StructElement()67 AppleObjCTypeEncodingParser::StructElement::StructElement()
68 : name(""), type(clang::QualType()), bitfield(0) {}
69
70 AppleObjCTypeEncodingParser::StructElement
ReadStructElement(clang::ASTContext & ast_ctx,lldb_utility::StringLexer & type,bool for_expression)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
BuildStruct(clang::ASTContext & ast_ctx,lldb_utility::StringLexer & type,bool for_expression)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
BuildUnion(clang::ASTContext & ast_ctx,lldb_utility::StringLexer & type,bool for_expression)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
BuildAggregate(clang::ASTContext & ast_ctx,lldb_utility::StringLexer & type,bool for_expression,char opener,char closer,uint32_t kind)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. If the name has
109 // a < in it, we are going to abandon this. We're still obliged to parse it,
110 // so we just set a flag that means "Don't actually build anything."
111
112 const bool is_templated = name.find('<') != std::string::npos;
113
114 if (!type.NextIf('='))
115 return clang::QualType();
116 bool in_union = true;
117 std::vector<StructElement> elements;
118 while (in_union && type.HasAtLeast(1)) {
119 if (type.NextIf(closer)) {
120 in_union = false;
121 break;
122 } else {
123 auto element = ReadStructElement(ast_ctx, type, for_expression);
124 if (element.type.isNull())
125 break;
126 else
127 elements.push_back(element);
128 }
129 }
130 if (in_union)
131 return clang::QualType();
132
133 if (is_templated)
134 return clang::QualType(); // This is where we bail out. Sorry!
135
136 ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
137 if (!lldb_ctx)
138 return clang::QualType();
139 CompilerType union_type(lldb_ctx->CreateRecordType(
140 nullptr, lldb::eAccessPublic, name.c_str(), kind, lldb::eLanguageTypeC));
141 if (union_type) {
142 ClangASTContext::StartTagDeclarationDefinition(union_type);
143
144 unsigned int count = 0;
145 for (auto element : elements) {
146 if (element.name.empty()) {
147 StreamString elem_name;
148 elem_name.Printf("__unnamed_%u", count);
149 element.name = elem_name.GetString();
150 }
151 ClangASTContext::AddFieldToRecordType(
152 union_type, element.name.c_str(),
153 CompilerType(&ast_ctx, element.type), lldb::eAccessPublic,
154 element.bitfield);
155 ++count;
156 }
157 ClangASTContext::CompleteTagDeclarationDefinition(union_type);
158 }
159 return ClangUtil::GetQualType(union_type);
160 }
161
162 clang::QualType
BuildArray(clang::ASTContext & ast_ctx,lldb_utility::StringLexer & type,bool for_expression)163 AppleObjCTypeEncodingParser::BuildArray(clang::ASTContext &ast_ctx,
164 lldb_utility::StringLexer &type,
165 bool for_expression) {
166 if (!type.NextIf('['))
167 return clang::QualType();
168 uint32_t size = ReadNumber(type);
169 clang::QualType element_type(BuildType(ast_ctx, type, for_expression));
170 if (!type.NextIf(']'))
171 return clang::QualType();
172 ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
173 if (!lldb_ctx)
174 return clang::QualType();
175 CompilerType array_type(lldb_ctx->CreateArrayType(
176 CompilerType(&ast_ctx, element_type), size, false));
177 return ClangUtil::GetQualType(array_type);
178 }
179
180 // the runtime can emit these in the form of @"SomeType", giving more specifics
181 // this would be interesting for expression parser interop, but since we
182 // actually try to avoid exposing the ivar info to the expression evaluator,
183 // consume but ignore the type info and always return an 'id'; if anything,
184 // dynamic typing will resolve things for us anyway
BuildObjCObjectPointerType(clang::ASTContext & ast_ctx,lldb_utility::StringLexer & type,bool for_expression)185 clang::QualType AppleObjCTypeEncodingParser::BuildObjCObjectPointerType(
186 clang::ASTContext &ast_ctx, lldb_utility::StringLexer &type,
187 bool for_expression) {
188 if (!type.NextIf('@'))
189 return clang::QualType();
190
191 std::string name;
192
193 if (type.NextIf('"')) {
194 // We have to be careful here. We're used to seeing
195 // @"NSString"
196 // but in records it is possible that the string following an @ is the name
197 // of the next field and @ means "id". This is the case if anything
198 // unquoted except for "}", the end of the type, or another name follows
199 // the quoted string.
200 //
201 // E.g.
202 // - @"NSString"@ means "id, followed by a field named NSString of type id"
203 // - @"NSString"} means "a pointer to NSString and the end of the struct" -
204 // @"NSString""nextField" means "a pointer to NSString and a field named
205 // nextField" - @"NSString" followed by the end of the string means "a
206 // pointer to NSString"
207 //
208 // As a result, the rule is: If we see @ followed by a quoted string, we
209 // peek. - If we see }, ), ], the end of the string, or a quote ("), the
210 // quoted string is a class name. - If we see anything else, the quoted
211 // string is a field name and we push it back onto type.
212
213 name = ReadQuotedString(type);
214
215 if (type.HasAtLeast(1)) {
216 switch (type.Peek()) {
217 default:
218 // roll back
219 type.PutBack(name.length() +
220 2); // undo our consumption of the string and of the quotes
221 name.clear();
222 break;
223 case '}':
224 case ')':
225 case ']':
226 case '"':
227 // the quoted string is a class name – see the rule
228 break;
229 }
230 } else {
231 // the quoted string is a class name – see the rule
232 }
233 }
234
235 if (for_expression && !name.empty()) {
236 size_t less_than_pos = name.find('<');
237
238 if (less_than_pos != std::string::npos) {
239 if (less_than_pos == 0)
240 return ast_ctx.getObjCIdType();
241 else
242 name.erase(less_than_pos);
243 }
244
245 DeclVendor *decl_vendor = m_runtime.GetDeclVendor();
246 if (!decl_vendor)
247 return clang::QualType();
248
249 const bool append = false;
250 const uint32_t max_matches = 1;
251 std::vector<clang::NamedDecl *> decls;
252
253 uint32_t num_types =
254 decl_vendor->FindDecls(ConstString(name), append, max_matches, decls);
255
256 // The user can forward-declare something that has no definition. The runtime
257 // doesn't prohibit this at all. This is a rare and very weird case. We keep
258 // this assert in debug builds so we catch other weird cases.
259 #ifdef LLDB_CONFIGURATION_DEBUG
260 assert(num_types);
261 #else
262 if (!num_types)
263 return ast_ctx.getObjCIdType();
264 #endif
265
266 return ClangUtil::GetQualType(
267 ClangASTContext::GetTypeForDecl(decls[0]).GetPointerType());
268 } else {
269 // We're going to resolve this dynamically anyway, so just smile and wave.
270 return ast_ctx.getObjCIdType();
271 }
272 }
273
274 clang::QualType
BuildType(clang::ASTContext & ast_ctx,StringLexer & type,bool for_expression,uint32_t * bitfield_bit_size)275 AppleObjCTypeEncodingParser::BuildType(clang::ASTContext &ast_ctx,
276 StringLexer &type, bool for_expression,
277 uint32_t *bitfield_bit_size) {
278 if (!type.HasAtLeast(1))
279 return clang::QualType();
280
281 switch (type.Peek()) {
282 default:
283 break;
284 case '{':
285 return BuildStruct(ast_ctx, type, for_expression);
286 case '[':
287 return BuildArray(ast_ctx, type, for_expression);
288 case '(':
289 return BuildUnion(ast_ctx, type, for_expression);
290 case '@':
291 return BuildObjCObjectPointerType(ast_ctx, type, for_expression);
292 }
293
294 switch (type.Next()) {
295 default:
296 type.PutBack(1);
297 return clang::QualType();
298 case 'c':
299 return ast_ctx.CharTy;
300 case 'i':
301 return ast_ctx.IntTy;
302 case 's':
303 return ast_ctx.ShortTy;
304 case 'l':
305 return ast_ctx.getIntTypeForBitwidth(32, true);
306 // this used to be done like this:
307 // ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
308 // if (!lldb_ctx)
309 // return clang::QualType();
310 // return lldb_ctx->GetIntTypeFromBitSize(32, true).GetQualType();
311 // which uses one of the constants if one is available, but we don't think
312 // all this work is necessary.
313 case 'q':
314 return ast_ctx.LongLongTy;
315 case 'C':
316 return ast_ctx.UnsignedCharTy;
317 case 'I':
318 return ast_ctx.UnsignedIntTy;
319 case 'S':
320 return ast_ctx.UnsignedShortTy;
321 case 'L':
322 return ast_ctx.getIntTypeForBitwidth(32, false);
323 // see note for 'l'
324 case 'Q':
325 return ast_ctx.UnsignedLongLongTy;
326 case 'f':
327 return ast_ctx.FloatTy;
328 case 'd':
329 return ast_ctx.DoubleTy;
330 case 'B':
331 return ast_ctx.BoolTy;
332 case 'v':
333 return ast_ctx.VoidTy;
334 case '*':
335 return ast_ctx.getPointerType(ast_ctx.CharTy);
336 case '#':
337 return ast_ctx.getObjCClassType();
338 case ':':
339 return ast_ctx.getObjCSelType();
340 case 'b': {
341 uint32_t size = ReadNumber(type);
342 if (bitfield_bit_size) {
343 *bitfield_bit_size = size;
344 return ast_ctx.UnsignedIntTy; // FIXME: the spec is fairly vague here.
345 } else
346 return clang::QualType();
347 }
348 case 'r': {
349 clang::QualType target_type = BuildType(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.getConstType(target_type);
356 }
357 case '^': {
358 if (!for_expression && type.NextIf('?')) {
359 // if we are not supporting the concept of unknownAny, but what is being
360 // created here is an unknownAny*, then we can just get away with a void*
361 // this is theoretically wrong (in the same sense as 'theoretically
362 // nothing exists') but is way better than outright failure in many
363 // practical cases
364 return ast_ctx.VoidPtrTy;
365 } else {
366 clang::QualType target_type = BuildType(ast_ctx, type, for_expression);
367 if (target_type.isNull())
368 return clang::QualType();
369 else if (target_type == ast_ctx.UnknownAnyTy)
370 return ast_ctx.UnknownAnyTy;
371 else
372 return ast_ctx.getPointerType(target_type);
373 }
374 }
375 case '?':
376 return for_expression ? ast_ctx.UnknownAnyTy : clang::QualType();
377 }
378 }
379
RealizeType(clang::ASTContext & ast_ctx,const char * name,bool for_expression)380 CompilerType AppleObjCTypeEncodingParser::RealizeType(
381 clang::ASTContext &ast_ctx, const char *name, bool for_expression) {
382 if (name && name[0]) {
383 StringLexer lexer(name);
384 clang::QualType qual_type = BuildType(ast_ctx, lexer, for_expression);
385 return CompilerType(&ast_ctx, qual_type);
386 }
387 return CompilerType();
388 }
389