1 //===-- AppleObjCTypeEncodingParser.cpp -------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "AppleObjCTypeEncodingParser.h"
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"
20 using namespace lldb_private;
21 using namespace lldb_utility;
23 AppleObjCTypeEncodingParser::AppleObjCTypeEncodingParser(
24 ObjCLanguageRuntime &runtime)
25 : ObjCLanguageRuntime::EncodingToType(), m_runtime(runtime) {
26 if (!m_scratch_ast_ctx_up)
27 m_scratch_ast_ctx_up.reset(new ClangASTContext(runtime.GetProcess()
36 AppleObjCTypeEncodingParser::ReadStructName(lldb_utility::StringLexer &type) {
38 while (type.HasAtLeast(1) && type.Peek() != '=')
39 buffer.Printf("%c", type.Next());
40 return buffer.GetString();
44 AppleObjCTypeEncodingParser::ReadQuotedString(lldb_utility::StringLexer &type) {
46 while (type.HasAtLeast(1) && type.Peek() != '"')
47 buffer.Printf("%c", type.Next());
48 StringLexer::Character next = type.Next();
49 UNUSED_IF_ASSERT_DISABLED(next);
51 return buffer.GetString();
55 AppleObjCTypeEncodingParser::ReadNumber(lldb_utility::StringLexer &type) {
57 while (type.HasAtLeast(1) && isdigit(type.Peek()))
58 total = 10 * total + (type.Next() - '0');
62 // as an extension to the published grammar recent runtimes emit structs like
64 // "{CGRect=\"origin\"{CGPoint=\"x\"d\"y\"d}\"size\"{CGSize=\"width\"d\"height\"d}}"
66 AppleObjCTypeEncodingParser::StructElement::StructElement()
67 : name(""), type(clang::QualType()), bitfield(0) {}
69 AppleObjCTypeEncodingParser::StructElement
70 AppleObjCTypeEncodingParser::ReadStructElement(clang::ASTContext &ast_ctx,
71 lldb_utility::StringLexer &type,
72 bool for_expression) {
75 retval.name = ReadQuotedString(type);
76 if (!type.NextIf('"'))
78 uint32_t bitfield_size = 0;
79 retval.type = BuildType(ast_ctx, type, for_expression, &bitfield_size);
80 retval.bitfield = bitfield_size;
85 AppleObjCTypeEncodingParser::BuildStruct(clang::ASTContext &ast_ctx,
86 lldb_utility::StringLexer &type,
87 bool for_expression) {
88 return BuildAggregate(ast_ctx, type, for_expression, '{', '}',
93 AppleObjCTypeEncodingParser::BuildUnion(clang::ASTContext &ast_ctx,
94 lldb_utility::StringLexer &type,
95 bool for_expression) {
96 return BuildAggregate(ast_ctx, type, for_expression, '(', ')',
100 clang::QualType AppleObjCTypeEncodingParser::BuildAggregate(
101 clang::ASTContext &ast_ctx, lldb_utility::StringLexer &type,
102 bool for_expression, char opener, char closer, uint32_t kind) {
103 if (!type.NextIf(opener))
104 return clang::QualType();
105 std::string name(ReadStructName(type));
107 // We do not handle templated classes/structs at the moment. If the name has
108 // a < in it, we are going to abandon this. We're still obliged to parse it,
109 // so we just set a flag that means "Don't actually build anything."
111 const bool is_templated = name.find('<') != std::string::npos;
113 if (!type.NextIf('='))
114 return clang::QualType();
115 bool in_union = true;
116 std::vector<StructElement> elements;
117 while (in_union && type.HasAtLeast(1)) {
118 if (type.NextIf(closer)) {
122 auto element = ReadStructElement(ast_ctx, type, for_expression);
123 if (element.type.isNull())
126 elements.push_back(element);
130 return clang::QualType();
133 return clang::QualType(); // This is where we bail out. Sorry!
135 ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
137 return clang::QualType();
138 CompilerType union_type(lldb_ctx->CreateRecordType(
139 nullptr, lldb::eAccessPublic, name.c_str(), kind, lldb::eLanguageTypeC));
141 ClangASTContext::StartTagDeclarationDefinition(union_type);
143 unsigned int count = 0;
144 for (auto element : elements) {
145 if (element.name.empty()) {
146 StreamString elem_name;
147 elem_name.Printf("__unnamed_%u", count);
148 element.name = elem_name.GetString();
150 ClangASTContext::AddFieldToRecordType(
151 union_type, element.name.c_str(),
152 CompilerType(&ast_ctx, element.type), lldb::eAccessPublic,
156 ClangASTContext::CompleteTagDeclarationDefinition(union_type);
158 return ClangUtil::GetQualType(union_type);
162 AppleObjCTypeEncodingParser::BuildArray(clang::ASTContext &ast_ctx,
163 lldb_utility::StringLexer &type,
164 bool for_expression) {
165 if (!type.NextIf('['))
166 return clang::QualType();
167 uint32_t size = ReadNumber(type);
168 clang::QualType element_type(BuildType(ast_ctx, type, for_expression));
169 if (!type.NextIf(']'))
170 return clang::QualType();
171 ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
173 return clang::QualType();
174 CompilerType array_type(lldb_ctx->CreateArrayType(
175 CompilerType(&ast_ctx, element_type), size, false));
176 return ClangUtil::GetQualType(array_type);
179 // the runtime can emit these in the form of @"SomeType", giving more specifics
180 // this would be interesting for expression parser interop, but since we
181 // actually try to avoid exposing the ivar info to the expression evaluator,
182 // consume but ignore the type info and always return an 'id'; if anything,
183 // dynamic typing will resolve things for us anyway
184 clang::QualType AppleObjCTypeEncodingParser::BuildObjCObjectPointerType(
185 clang::ASTContext &ast_ctx, lldb_utility::StringLexer &type,
186 bool for_expression) {
187 if (!type.NextIf('@'))
188 return clang::QualType();
192 if (type.NextIf('"')) {
193 // We have to be careful here. We're used to seeing
195 // but in records it is possible that the string following an @ is the name
196 // of the next field and @ means "id". This is the case if anything
197 // unquoted except for "}", the end of the type, or another name follows
198 // the quoted string.
201 // - @"NSString"@ means "id, followed by a field named NSString of type id"
202 // - @"NSString"} means "a pointer to NSString and the end of the struct" -
203 // @"NSString""nextField" means "a pointer to NSString and a field named
204 // nextField" - @"NSString" followed by the end of the string means "a
205 // pointer to NSString"
207 // As a result, the rule is: If we see @ followed by a quoted string, we
208 // peek. - If we see }, ), ], the end of the string, or a quote ("), the
209 // quoted string is a class name. - If we see anything else, the quoted
210 // string is a field name and we push it back onto type.
212 name = ReadQuotedString(type);
214 if (type.HasAtLeast(1)) {
215 switch (type.Peek()) {
218 type.PutBack(name.length() +
219 2); // undo our consumption of the string and of the quotes
226 // the quoted string is a class name – see the rule
230 // the quoted string is a class name – see the rule
234 if (for_expression && !name.empty()) {
235 size_t less_than_pos = name.find('<');
237 if (less_than_pos != std::string::npos) {
238 if (less_than_pos == 0)
239 return ast_ctx.getObjCIdType();
241 name.erase(less_than_pos);
244 DeclVendor *decl_vendor = m_runtime.GetDeclVendor();
246 return clang::QualType();
248 auto types = decl_vendor->FindTypes(ConstString(name), /*max_matches*/ 1);
250 // The user can forward-declare something that has no definition. The runtime
251 // doesn't prohibit this at all. This is a rare and very weird case. We keep
252 // this assert in debug builds so we catch other weird cases.
253 #ifdef LLDB_CONFIGURATION_DEBUG
254 assert(!types.empty());
257 return ast_ctx.getObjCIdType();
260 return ClangUtil::GetQualType(types.front().GetPointerType());
262 // We're going to resolve this dynamically anyway, so just smile and wave.
263 return ast_ctx.getObjCIdType();
268 AppleObjCTypeEncodingParser::BuildType(clang::ASTContext &ast_ctx,
269 StringLexer &type, bool for_expression,
270 uint32_t *bitfield_bit_size) {
271 if (!type.HasAtLeast(1))
272 return clang::QualType();
274 switch (type.Peek()) {
278 return BuildStruct(ast_ctx, type, for_expression);
280 return BuildArray(ast_ctx, type, for_expression);
282 return BuildUnion(ast_ctx, type, for_expression);
284 return BuildObjCObjectPointerType(ast_ctx, type, for_expression);
287 switch (type.Next()) {
290 return clang::QualType();
292 return ast_ctx.CharTy;
294 return ast_ctx.IntTy;
296 return ast_ctx.ShortTy;
298 return ast_ctx.getIntTypeForBitwidth(32, true);
299 // this used to be done like this:
300 // ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
302 // return clang::QualType();
303 // return lldb_ctx->GetIntTypeFromBitSize(32, true).GetQualType();
304 // which uses one of the constants if one is available, but we don't think
305 // all this work is necessary.
307 return ast_ctx.LongLongTy;
309 return ast_ctx.UnsignedCharTy;
311 return ast_ctx.UnsignedIntTy;
313 return ast_ctx.UnsignedShortTy;
315 return ast_ctx.getIntTypeForBitwidth(32, false);
318 return ast_ctx.UnsignedLongLongTy;
320 return ast_ctx.FloatTy;
322 return ast_ctx.DoubleTy;
324 return ast_ctx.BoolTy;
326 return ast_ctx.VoidTy;
328 return ast_ctx.getPointerType(ast_ctx.CharTy);
330 return ast_ctx.getObjCClassType();
332 return ast_ctx.getObjCSelType();
334 uint32_t size = ReadNumber(type);
335 if (bitfield_bit_size) {
336 *bitfield_bit_size = size;
337 return ast_ctx.UnsignedIntTy; // FIXME: the spec is fairly vague here.
339 return clang::QualType();
342 clang::QualType target_type = BuildType(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;
348 return ast_ctx.getConstType(target_type);
351 if (!for_expression && type.NextIf('?')) {
352 // if we are not supporting the concept of unknownAny, but what is being
353 // created here is an unknownAny*, then we can just get away with a void*
354 // this is theoretically wrong (in the same sense as 'theoretically
355 // nothing exists') but is way better than outright failure in many
357 return ast_ctx.VoidPtrTy;
359 clang::QualType target_type = BuildType(ast_ctx, type, for_expression);
360 if (target_type.isNull())
361 return clang::QualType();
362 else if (target_type == ast_ctx.UnknownAnyTy)
363 return ast_ctx.UnknownAnyTy;
365 return ast_ctx.getPointerType(target_type);
369 return for_expression ? ast_ctx.UnknownAnyTy : clang::QualType();
373 CompilerType AppleObjCTypeEncodingParser::RealizeType(
374 clang::ASTContext &ast_ctx, const char *name, bool for_expression) {
375 if (name && name[0]) {
376 StringLexer lexer(name);
377 clang::QualType qual_type = BuildType(ast_ctx, lexer, for_expression);
378 return CompilerType(&ast_ctx, qual_type);
380 return CompilerType();