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