LLDB mainline
AppleObjCTypeEncodingParser.cpp
Go to the documentation of this file.
1//===-- AppleObjCTypeEncodingParser.cpp -----------------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
10
14#include "lldb/Target/Process.h"
15#include "lldb/Target/Target.h"
17#include "lldb/Utility/Log.h"
19
20#include "clang/Basic/TargetInfo.h"
21
22#include <vector>
23
24using namespace lldb_private;
25
27 ObjCLanguageRuntime &runtime)
28 : ObjCLanguageRuntime::EncodingToType(), m_runtime(runtime) {
30 return;
31
32 m_scratch_ast_ctx_sp = std::make_shared<TypeSystemClang>(
33 "AppleObjCTypeEncodingParser ASTContext",
35}
36
38 StreamString buffer;
39 while (type.HasAtLeast(1) && type.Peek() != '=')
40 buffer.Printf("%c", type.Next());
41 return std::string(buffer.GetString());
42}
43
45 StreamString buffer;
46 while (type.HasAtLeast(1) && type.Peek() != '"')
47 buffer.Printf("%c", type.Next());
48 StringLexer::Character next = type.Next();
50 assert(next == '"');
51 return std::string(buffer.GetString());
52}
53
55 uint32_t total = 0;
56 while (type.HasAtLeast(1) && isdigit(type.Peek()))
57 total = 10 * total + (type.Next() - '0');
58 return total;
59}
60
61// as an extension to the published grammar recent runtimes emit structs like
62// this:
63// "{CGRect=\"origin\"{CGPoint=\"x\"d\"y\"d}\"size\"{CGSize=\"width\"d\"height\"d}}"
64
66 : type(clang::QualType()) {}
67
70 StringLexer &type,
71 bool for_expression) {
72 StructElement retval;
73 if (type.NextIf('"'))
74 retval.name = ReadQuotedString(type);
75 if (!type.NextIf('"'))
76 return retval;
77 uint32_t bitfield_size = 0;
78 retval.type = BuildType(ast_ctx, type, for_expression, &bitfield_size);
79 retval.bitfield = bitfield_size;
80 return retval;
81}
82
84 TypeSystemClang &ast_ctx, StringLexer &type, bool for_expression) {
85 return BuildAggregate(ast_ctx, type, for_expression, _C_STRUCT_B, _C_STRUCT_E,
86 llvm::to_underlying(clang::TagTypeKind::Struct));
87}
88
90 TypeSystemClang &ast_ctx, StringLexer &type, bool for_expression) {
91 return BuildAggregate(ast_ctx, type, for_expression, _C_UNION_B, _C_UNION_E,
92 llvm::to_underlying(clang::TagTypeKind::Union));
93}
94
96 TypeSystemClang &ast_ctx, StringLexer &type, bool for_expression,
97 char opener, char closer, uint32_t kind) {
98 if (!type.NextIf(opener))
99 return clang::QualType();
100 std::string name(ReadStructName(type));
101
102 // We do not handle templated classes/structs at the moment. If the name has
103 // a < in it, we are going to abandon this. We're still obliged to parse it,
104 // so we just set a flag that means "Don't actually build anything."
105
106 const bool is_templated = name.find('<') != std::string::npos;
107
108 if (!type.NextIf('='))
109 return clang::QualType();
110 bool in_union = true;
111 std::vector<StructElement> elements;
112 while (in_union && type.HasAtLeast(1)) {
113 if (type.NextIf(closer)) {
114 in_union = false;
115 break;
116 } else {
117 auto element = ReadStructElement(ast_ctx, type, for_expression);
118 if (element.type.isNull())
119 break;
120 else
121 elements.push_back(element);
122 }
123 }
124 if (in_union)
125 return clang::QualType();
126
127 if (is_templated)
128 return clang::QualType(); // This is where we bail out. Sorry!
129
130 CompilerType union_type(ast_ctx.CreateRecordType(
131 nullptr, OptionalClangModuleID(), lldb::eAccessPublic, name, kind,
133 if (union_type) {
135
136 unsigned int count = 0;
137 for (auto element : elements) {
138 if (element.name.empty()) {
139 StreamString elem_name;
140 elem_name.Printf("__unnamed_%u", count);
141 element.name = std::string(elem_name.GetString());
142 }
144 union_type, element.name.c_str(), ast_ctx.GetType(element.type),
145 lldb::eAccessPublic, element.bitfield);
146 ++count;
147 }
149 }
150 return ClangUtil::GetQualType(union_type);
151}
152
154 TypeSystemClang &ast_ctx, StringLexer &type, bool for_expression) {
155 if (!type.NextIf(_C_ARY_B))
156 return clang::QualType();
157 uint32_t size = ReadNumber(type);
158 clang::QualType element_type(BuildType(ast_ctx, type, for_expression));
159 if (!type.NextIf(_C_ARY_E))
160 return clang::QualType();
161 CompilerType array_type(ast_ctx.CreateArrayType(
162 CompilerType(ast_ctx.weak_from_this(), element_type.getAsOpaquePtr()),
163 size, false));
164 return ClangUtil::GetQualType(array_type);
165}
166
167// the runtime can emit these in the form of @"SomeType", giving more specifics
168// this would be interesting for expression parser interop, but since we
169// actually try to avoid exposing the ivar info to the expression evaluator,
170// consume but ignore the type info and always return an 'id'; if anything,
171// dynamic typing will resolve things for us anyway
173 TypeSystemClang &clang_ast_ctx, StringLexer &type, bool for_expression) {
174 if (!type.NextIf(_C_ID))
175 return clang::QualType();
176
177 clang::ASTContext &ast_ctx = clang_ast_ctx.getASTContext();
178
179 std::string name;
180
181 if (type.NextIf('"')) {
182 // We have to be careful here. We're used to seeing
183 // @"NSString"
184 // but in records it is possible that the string following an @ is the name
185 // of the next field and @ means "id". This is the case if anything
186 // unquoted except for "}", the end of the type, or another name follows
187 // the quoted string.
188 //
189 // E.g.
190 // - @"NSString"@ means "id, followed by a field named NSString of type id"
191 // - @"NSString"} means "a pointer to NSString and the end of the struct" -
192 // @"NSString""nextField" means "a pointer to NSString and a field named
193 // nextField" - @"NSString" followed by the end of the string means "a
194 // pointer to NSString"
195 //
196 // As a result, the rule is: If we see @ followed by a quoted string, we
197 // peek. - If we see }, ), ], the end of the string, or a quote ("), the
198 // quoted string is a class name. - If we see anything else, the quoted
199 // string is a field name and we push it back onto type.
200
201 name = ReadQuotedString(type);
202
203 if (type.HasAtLeast(1)) {
204 switch (type.Peek()) {
205 default:
206 // roll back
207 type.PutBack(name.length() +
208 2); // undo our consumption of the string and of the quotes
209 name.clear();
210 break;
211 case _C_STRUCT_E:
212 case _C_UNION_E:
213 case _C_ARY_E:
214 case '"':
215 // the quoted string is a class name – see the rule
216 break;
217 }
218 } else {
219 // the quoted string is a class name – see the rule
220 }
221 }
222
223 if (for_expression && !name.empty()) {
224 size_t less_than_pos = name.find('<');
225
226 if (less_than_pos != std::string::npos) {
227 if (less_than_pos == 0)
228 return ast_ctx.getObjCIdType();
229 else
230 name.erase(less_than_pos);
231 }
232
233 DeclVendor *decl_vendor = m_runtime.GetDeclVendor();
234 if (!decl_vendor)
235 return clang::QualType();
236
237 auto types = decl_vendor->FindTypes(ConstString(name), /*max_matches*/ 1);
238
239 if (types.empty()) {
240 // The user can forward-declare something that has no definition. The
241 // runtime doesn't prohibit this at all. This is a rare and very weird
242 // case. Assert assert in debug builds so we catch other weird cases.
243 assert(false && "forward declaration without definition");
245 "forward declaration without definition: {0}", name);
246 return ast_ctx.getObjCIdType();
247 }
248
249 return ClangUtil::GetQualType(types.front().GetPointerType());
250 } else {
251 // We're going to resolve this dynamically anyway, so just smile and wave.
252 return ast_ctx.getObjCIdType();
253 }
254}
255
256clang::QualType
258 StringLexer &type, bool for_expression,
259 uint32_t *bitfield_bit_size) {
260 if (!type.HasAtLeast(1))
261 return clang::QualType();
262
263 clang::ASTContext &ast_ctx = clang_ast_ctx.getASTContext();
264
265 switch (type.Peek()) {
266 default:
267 break;
268 case _C_STRUCT_B:
269 return BuildStruct(clang_ast_ctx, type, for_expression);
270 case _C_ARY_B:
271 return BuildArray(clang_ast_ctx, type, for_expression);
272 case _C_UNION_B:
273 return BuildUnion(clang_ast_ctx, type, for_expression);
274 case _C_ID:
275 return BuildObjCObjectPointerType(clang_ast_ctx, type, for_expression);
276 }
277
278 switch (type.Next()) {
279 default:
280 type.PutBack(1);
281 return clang::QualType();
282 case _C_CHR:
283 return ast_ctx.CharTy;
284 case _C_INT:
285 return ast_ctx.IntTy;
286 case _C_SHT:
287 return ast_ctx.ShortTy;
288 case _C_LNG:
289 return ast_ctx.getIntTypeForBitwidth(32, true);
290 // this used to be done like this:
291 // return clang_ast_ctx->GetIntTypeFromBitSize(32, true).GetQualType();
292 // which uses one of the constants if one is available, but we don't think
293 // all this work is necessary.
294 case _C_LNG_LNG:
295 return ast_ctx.LongLongTy;
296 case _C_UCHR:
297 return ast_ctx.UnsignedCharTy;
298 case _C_UINT:
299 return ast_ctx.UnsignedIntTy;
300 case _C_USHT:
301 return ast_ctx.UnsignedShortTy;
302 case _C_ULNG:
303 return ast_ctx.getIntTypeForBitwidth(32, false);
304 // see note for _C_LNG
305 case _C_ULNG_LNG:
306 return ast_ctx.UnsignedLongLongTy;
307 case _C_FLT:
308 return ast_ctx.FloatTy;
309 case _C_DBL:
310 return ast_ctx.DoubleTy;
311 case _C_BOOL:
312 return ast_ctx.BoolTy;
313 case _C_VOID:
314 return ast_ctx.VoidTy;
315 case _C_CHARPTR:
316 return ast_ctx.getPointerType(ast_ctx.CharTy);
317 case _C_CLASS:
318 return ast_ctx.getObjCClassType();
319 case _C_SEL:
320 return ast_ctx.getObjCSelType();
321 case _C_BFLD: {
322 uint32_t size = ReadNumber(type);
323 if (bitfield_bit_size) {
324 *bitfield_bit_size = size;
325 return ast_ctx.UnsignedIntTy; // FIXME: the spec is fairly vague here.
326 } else
327 return clang::QualType();
328 }
329 case _C_CONST: {
330 clang::QualType target_type =
331 BuildType(clang_ast_ctx, type, for_expression);
332 if (target_type.isNull())
333 return clang::QualType();
334 else if (target_type == ast_ctx.UnknownAnyTy)
335 return ast_ctx.UnknownAnyTy;
336 else
337 return ast_ctx.getConstType(target_type);
338 }
339 case _C_PTR: {
340 if (!for_expression && type.NextIf(_C_UNDEF)) {
341 // if we are not supporting the concept of unknownAny, but what is being
342 // created here is an unknownAny*, then we can just get away with a void*
343 // this is theoretically wrong (in the same sense as 'theoretically
344 // nothing exists') but is way better than outright failure in many
345 // practical cases
346 return ast_ctx.VoidPtrTy;
347 } else {
348 clang::QualType target_type =
349 BuildType(clang_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.getPointerType(target_type);
356 }
357 }
358 case _C_UNDEF:
359 return for_expression ? ast_ctx.UnknownAnyTy : clang::QualType();
360 }
361}
362
364 const char *name,
365 bool for_expression) {
366 if (name && name[0]) {
367 StringLexer lexer(name);
368 clang::QualType qual_type = BuildType(ast_ctx, lexer, for_expression);
369 return ast_ctx.GetType(qual_type);
370 }
371 return CompilerType();
372}
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition: Log.h:359
#define _C_ARY_B
Definition: ObjCConstants.h:35
#define _C_CONST
Definition: ObjCConstants.h:42
#define _C_ID
Definition: ObjCConstants.h:13
#define _C_USHT
Definition: ObjCConstants.h:19
#define _C_STRUCT_B
Definition: ObjCConstants.h:39
#define _C_ULNG
Definition: ObjCConstants.h:23
#define _C_BOOL
Definition: ObjCConstants.h:29
#define _C_CHARPTR
Definition: ObjCConstants.h:33
#define _C_STRUCT_E
Definition: ObjCConstants.h:40
#define _C_INT
Definition: ObjCConstants.h:20
#define _C_DBL
Definition: ObjCConstants.h:27
#define _C_UNION_E
Definition: ObjCConstants.h:38
#define _C_UNION_B
Definition: ObjCConstants.h:37
#define _C_BFLD
Definition: ObjCConstants.h:28
#define _C_UINT
Definition: ObjCConstants.h:21
#define _C_CHR
Definition: ObjCConstants.h:16
#define _C_ULNG_LNG
Definition: ObjCConstants.h:25
#define _C_VOID
Definition: ObjCConstants.h:30
#define _C_ARY_E
Definition: ObjCConstants.h:36
#define _C_CLASS
Definition: ObjCConstants.h:14
#define _C_UCHR
Definition: ObjCConstants.h:17
#define _C_LNG
Definition: ObjCConstants.h:22
#define _C_LNG_LNG
Definition: ObjCConstants.h:24
#define _C_FLT
Definition: ObjCConstants.h:26
#define _C_PTR
Definition: ObjCConstants.h:32
#define _C_SHT
Definition: ObjCConstants.h:18
#define _C_SEL
Definition: ObjCConstants.h:15
#define _C_UNDEF
Definition: ObjCConstants.h:31
clang::QualType BuildType(TypeSystemClang &clang_ast_ctx, StringLexer &type, bool for_expression, uint32_t *bitfield_bit_size=nullptr)
clang::QualType BuildObjCObjectPointerType(TypeSystemClang &clang_ast_ctx, StringLexer &type, bool for_expression)
CompilerType RealizeType(TypeSystemClang &ast_ctx, const char *name, bool for_expression) override
clang::QualType BuildAggregate(TypeSystemClang &clang_ast_ctx, StringLexer &type, bool for_expression, char opener, char closer, uint32_t kind)
StructElement ReadStructElement(TypeSystemClang &ast_ctx, StringLexer &type, bool for_expression)
clang::QualType BuildArray(TypeSystemClang &ast_ctx, StringLexer &type, bool for_expression)
clang::QualType BuildStruct(TypeSystemClang &ast_ctx, StringLexer &type, bool for_expression)
clang::QualType BuildUnion(TypeSystemClang &ast_ctx, StringLexer &type, bool for_expression)
llvm::Triple & GetTriple()
Architecture triple accessor.
Definition: ArchSpec.h:450
Generic representation of a type in a programming language.
Definition: CompilerType.h:36
A uniqued constant string class.
Definition: ConstString.h:40
std::vector< CompilerType > FindTypes(ConstString name, uint32_t max_matches)
Look up the types that the DeclVendor currently knows about matching a given name.
Definition: DeclVendor.cpp:18
virtual DeclVendor * GetDeclVendor()
std::shared_ptr< TypeSystemClang > m_scratch_ast_ctx_sp
Target & GetTarget()
Get the target object pointer for this module.
Definition: Process.h:1285
Process * GetProcess()
Definition: Runtime.h:22
llvm::StringRef GetString() const
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition: Stream.cpp:134
std::string::value_type Character
Definition: StringLexer.h:23
bool NextIf(Character c)
Definition: StringLexer.cpp:21
const ArchSpec & GetArchitecture() const
Definition: Target.h:1028
A TypeSystem implementation based on Clang.
static clang::FieldDecl * AddFieldToRecordType(const CompilerType &type, llvm::StringRef name, const CompilerType &field_type, lldb::AccessType access, uint32_t bitfield_bit_size)
CompilerType CreateArrayType(const CompilerType &element_type, std::optional< size_t > element_count, bool is_vector)
CompilerType GetType(clang::QualType qt)
Creates a CompilerType from the given QualType with the current TypeSystemClang instance as the Compi...
static bool CompleteTagDeclarationDefinition(const CompilerType &type)
CompilerType CreateRecordType(clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, lldb::AccessType access_type, llvm::StringRef name, int kind, lldb::LanguageType language, std::optional< ClangASTMetadata > metadata=std::nullopt, bool exports_symbols=false)
static bool StartTagDeclarationDefinition(const CompilerType &type)
clang::ASTContext & getASTContext() const
Returns the clang::ASTContext instance managed by this TypeSystemClang.
#define UNUSED_IF_ASSERT_DISABLED(x)
Definition: lldb-defines.h:140
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition: Log.h:331
@ eLanguageTypeC
Non-standardized C, such as K&R.
static clang::QualType GetQualType(const CompilerType &ct)
Definition: ClangUtil.cpp:36