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