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. 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."
112 const bool is_templated = name.find('<') != std::string::npos;
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)) {
123 auto element = ReadStructElement(ast_ctx, type, for_expression);
124 if (element.type.isNull())
127 elements.push_back(element);
131 return clang::QualType();
134 return clang::QualType(); // This is where we bail out. Sorry!
136 ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
138 return clang::QualType();
139 CompilerType union_type(lldb_ctx->CreateRecordType(
140 nullptr, lldb::eAccessPublic, name.c_str(), kind, lldb::eLanguageTypeC));
142 ClangASTContext::StartTagDeclarationDefinition(union_type);
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();
151 ClangASTContext::AddFieldToRecordType(
152 union_type, element.name.c_str(),
153 CompilerType(&ast_ctx, element.type), lldb::eAccessPublic,
157 ClangASTContext::CompleteTagDeclarationDefinition(union_type);
159 return ClangUtil::GetQualType(union_type);
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);
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);
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
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();
193 if (type.NextIf('"')) {
194 // We have to be careful here. We're used to seeing
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.
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"
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.
213 name = ReadQuotedString(type);
215 if (type.HasAtLeast(1)) {
216 switch (type.Peek()) {
219 type.PutBack(name.length() +
220 2); // undo our consumption of the string and of the quotes
227 // the quoted string is a class name – see the rule
231 // the quoted string is a class name – see the rule
235 if (for_expression && !name.empty()) {
236 size_t less_than_pos = name.find('<');
238 if (less_than_pos != std::string::npos) {
239 if (less_than_pos == 0)
240 return ast_ctx.getObjCIdType();
242 name.erase(less_than_pos);
245 DeclVendor *decl_vendor = m_runtime.GetDeclVendor();
247 return clang::QualType();
249 const bool append = false;
250 const uint32_t max_matches = 1;
251 std::vector<clang::NamedDecl *> decls;
254 decl_vendor->FindDecls(ConstString(name), append, max_matches, decls);
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
263 return ast_ctx.getObjCIdType();
266 return ClangUtil::GetQualType(
267 ClangASTContext::GetTypeForDecl(decls[0]).GetPointerType());
269 // We're going to resolve this dynamically anyway, so just smile and wave.
270 return ast_ctx.getObjCIdType();
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();
281 switch (type.Peek()) {
285 return BuildStruct(ast_ctx, type, for_expression);
287 return BuildArray(ast_ctx, type, for_expression);
289 return BuildUnion(ast_ctx, type, for_expression);
291 return BuildObjCObjectPointerType(ast_ctx, type, for_expression);
294 switch (type.Next()) {
297 return clang::QualType();
299 return ast_ctx.CharTy;
301 return ast_ctx.IntTy;
303 return ast_ctx.ShortTy;
305 return ast_ctx.getIntTypeForBitwidth(32, true);
306 // this used to be done like this:
307 // ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_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.
314 return ast_ctx.LongLongTy;
316 return ast_ctx.UnsignedCharTy;
318 return ast_ctx.UnsignedIntTy;
320 return ast_ctx.UnsignedShortTy;
322 return ast_ctx.getIntTypeForBitwidth(32, false);
325 return ast_ctx.UnsignedLongLongTy;
327 return ast_ctx.FloatTy;
329 return ast_ctx.DoubleTy;
331 return ast_ctx.BoolTy;
333 return ast_ctx.VoidTy;
335 return ast_ctx.getPointerType(ast_ctx.CharTy);
337 return ast_ctx.getObjCClassType();
339 return ast_ctx.getObjCSelType();
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.
346 return clang::QualType();
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;
355 return ast_ctx.getConstType(target_type);
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
364 return ast_ctx.VoidPtrTy;
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;
372 return ast_ctx.getPointerType(target_type);
376 return for_expression ? ast_ctx.UnknownAnyTy : clang::QualType();
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);
387 return CompilerType();