1 //===-- AppleObjCTypeEncodingParser.cpp -------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "AppleObjCTypeEncodingParser.h"
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"
21 using namespace lldb_private;
22 using namespace lldb_utility;
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()
37 AppleObjCTypeEncodingParser::ReadStructName(lldb_utility::StringLexer &type) {
39 while (type.HasAtLeast(1) && type.Peek() != '=')
40 buffer.Printf("%c", type.Next());
41 return buffer.GetString();
45 AppleObjCTypeEncodingParser::ReadQuotedString(lldb_utility::StringLexer &type) {
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);
52 return buffer.GetString();
56 AppleObjCTypeEncodingParser::ReadNumber(lldb_utility::StringLexer &type) {
58 while (type.HasAtLeast(1) && isdigit(type.Peek()))
59 total = 10 * total + (type.Next() - '0');
63 // as an extension to the published grammar recent runtimes emit structs like
65 // "{CGRect=\"origin\"{CGPoint=\"x\"d\"y\"d}\"size\"{CGSize=\"width\"d\"height\"d}}"
67 AppleObjCTypeEncodingParser::StructElement::StructElement()
68 : name(""), type(clang::QualType()), bitfield(0) {}
70 AppleObjCTypeEncodingParser::StructElement
71 AppleObjCTypeEncodingParser::ReadStructElement(clang::ASTContext &ast_ctx,
72 lldb_utility::StringLexer &type,
73 bool for_expression) {
76 retval.name = ReadQuotedString(type);
77 if (!type.NextIf('"'))
79 uint32_t bitfield_size = 0;
80 retval.type = BuildType(ast_ctx, type, for_expression, &bitfield_size);
81 retval.bitfield = bitfield_size;
86 AppleObjCTypeEncodingParser::BuildStruct(clang::ASTContext &ast_ctx,
87 lldb_utility::StringLexer &type,
88 bool for_expression) {
89 return BuildAggregate(ast_ctx, type, 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, '(', ')',
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));
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."
113 const bool is_templated = name.find('<') != std::string::npos;
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)) {
124 auto element = ReadStructElement(ast_ctx, type, for_expression);
125 if (element.type.isNull())
128 elements.push_back(element);
132 return clang::QualType();
135 return clang::QualType(); // This is where we bail out. Sorry!
137 ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
139 return clang::QualType();
140 CompilerType union_type(lldb_ctx->CreateRecordType(
141 nullptr, lldb::eAccessPublic, name.c_str(), kind, lldb::eLanguageTypeC));
143 ClangASTContext::StartTagDeclarationDefinition(union_type);
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();
152 ClangASTContext::AddFieldToRecordType(
153 union_type, element.name.c_str(),
154 CompilerType(&ast_ctx, element.type), lldb::eAccessPublic,
158 ClangASTContext::CompleteTagDeclarationDefinition(union_type);
160 return ClangUtil::GetQualType(union_type);
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);
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);
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
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
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();
196 if (type.NextIf('"')) {
197 // We have to be careful here. We're used to seeing
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.
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
209 // - @"NSString" followed by the end of the string means "a pointer to
212 // As a result, the rule is: If we see @ followed by a quoted string, we
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.
219 name = ReadQuotedString(type);
221 if (type.HasAtLeast(1)) {
222 switch (type.Peek()) {
225 type.PutBack(name.length() +
226 2); // undo our consumption of the string and of the quotes
233 // the quoted string is a class name – see the rule
237 // the quoted string is a class name – see the rule
241 if (for_expression && !name.empty()) {
242 size_t less_than_pos = name.find('<');
244 if (less_than_pos != std::string::npos) {
245 if (less_than_pos == 0)
246 return ast_ctx.getObjCIdType();
248 name.erase(less_than_pos);
251 DeclVendor *decl_vendor = m_runtime.GetDeclVendor();
253 return clang::QualType();
255 const bool append = false;
256 const uint32_t max_matches = 1;
257 std::vector<clang::NamedDecl *> decls;
260 decl_vendor->FindDecls(ConstString(name), append, max_matches, decls);
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
270 return ast_ctx.getObjCIdType();
273 return ClangUtil::GetQualType(
274 ClangASTContext::GetTypeForDecl(decls[0]).GetPointerType());
276 // We're going to resolve this dynamically anyway, so just smile and wave.
277 return ast_ctx.getObjCIdType();
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();
288 switch (type.Peek()) {
292 return BuildStruct(ast_ctx, type, for_expression);
294 return BuildArray(ast_ctx, type, for_expression);
296 return BuildUnion(ast_ctx, type, for_expression);
298 return BuildObjCObjectPointerType(ast_ctx, type, for_expression);
301 switch (type.Next()) {
304 return clang::QualType();
306 return ast_ctx.CharTy;
308 return ast_ctx.IntTy;
310 return ast_ctx.ShortTy;
312 return ast_ctx.getIntTypeForBitwidth(32, true);
313 // this used to be done like this:
314 // ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_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.
321 return ast_ctx.LongLongTy;
323 return ast_ctx.UnsignedCharTy;
325 return ast_ctx.UnsignedIntTy;
327 return ast_ctx.UnsignedShortTy;
329 return ast_ctx.getIntTypeForBitwidth(32, false);
332 return ast_ctx.UnsignedLongLongTy;
334 return ast_ctx.FloatTy;
336 return ast_ctx.DoubleTy;
338 return ast_ctx.BoolTy;
340 return ast_ctx.VoidTy;
342 return ast_ctx.getPointerType(ast_ctx.CharTy);
344 return ast_ctx.getObjCClassType();
346 return ast_ctx.getObjCSelType();
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.
353 return clang::QualType();
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;
362 return ast_ctx.getConstType(target_type);
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;
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;
380 return ast_ctx.getPointerType(target_type);
384 return for_expression ? ast_ctx.UnknownAnyTy : clang::QualType();
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);
395 return CompilerType();