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(), lldb::eAccessPublic, name, kind,
150 if (union_type) {
152
153 unsigned int count = 0;
154 for (auto element : elements) {
155 if (element.name.empty()) {
156 StreamString elem_name;
157 elem_name.Printf("__unnamed_%u", count);
158 element.name = std::string(elem_name.GetString());
159 }
161 union_type, element.name.c_str(), ast_ctx.GetType(element.type),
162 lldb::eAccessPublic, element.bitfield);
163 ++count;
164 }
166 }
167 return ClangUtil::GetQualType(union_type);
168}
169
171 TypeSystemClang &ast_ctx, llvm::StringRef &type, bool for_expression) {
172 if (!type.consume_front(_C_ARY_B))
173 return clang::QualType();
174
175 uint32_t size = ReadNumber(type);
176 clang::QualType element_type(BuildType(ast_ctx, type, for_expression));
177 if (!type.consume_front(_C_ARY_E))
178 return clang::QualType();
179
180 CompilerType array_type(ast_ctx.CreateArrayType(
181 CompilerType(ast_ctx.weak_from_this(), element_type.getAsOpaquePtr()),
182 size, false));
183 return ClangUtil::GetQualType(array_type);
184}
185
186// the runtime can emit these in the form of @"SomeType", giving more specifics
187// this would be interesting for expression parser interop, but since we
188// actually try to avoid exposing the ivar info to the expression evaluator,
189// consume but ignore the type info and always return an 'id'; if anything,
190// dynamic typing will resolve things for us anyway
192 TypeSystemClang &clang_ast_ctx, llvm::StringRef &type,
193 bool for_expression) {
194 if (!type.consume_front(_C_ID))
195 return clang::QualType();
196
197 clang::ASTContext &ast_ctx = clang_ast_ctx.getASTContext();
198
199 std::string name;
200
201 if (type.consume_front('"')) {
202 // We have to be careful here. We're used to seeing
203 // @"NSString"
204 // but in records it is possible that the string following an @ is the name
205 // of the next field and @ means "id". This is the case if anything
206 // unquoted except for "}", the end of the type, or another name follows
207 // the quoted string.
208 //
209 // E.g.
210 // - @"NSString"@ means "id, followed by a field named NSString of type id"
211 // - @"NSString"} means "a pointer to NSString and the end of the struct" -
212 // @"NSString""nextField" means "a pointer to NSString and a field named
213 // nextField" - @"NSString" followed by the end of the string means "a
214 // pointer to NSString"
215 //
216 // As a result, the rule is: If we see @ followed by a quoted string, we
217 // peek. - If we see }, ), ], the end of the string, or a quote ("), the
218 // quoted string is a class name. - If we see anything else, the quoted
219 // string is a field name and we push it back onto type.
220
221 // Save a copy for possible rollback.
222 llvm::StringRef backup = type;
223 if (auto maybe_name = ReadQuotedString(type))
224 name = *maybe_name;
225 else
226 return clang::QualType();
227
228 if (!type.empty()) {
229 switch (type.front()) {
230 default:
231 // roll back: undo our consumption of the string and of the quotes
232 type = backup;
233 name.clear();
234 break;
235 case _C_STRUCT_E:
236 case _C_UNION_E:
237 case _C_ARY_E:
238 case '"':
239 // the quoted string is a class name – see the rule
240 break;
241 }
242 } else {
243 // the quoted string is a class name – see the rule
244 }
245 }
246
247 if (for_expression && !name.empty()) {
248 size_t less_than_pos = name.find('<');
249
250 if (less_than_pos != std::string::npos) {
251 if (less_than_pos == 0)
252 return ast_ctx.getObjCIdType();
253 else
254 name.erase(less_than_pos);
255 }
256
257 DeclVendor *decl_vendor = m_runtime.GetDeclVendor();
258 if (!decl_vendor)
259 return clang::QualType();
260
261 auto types = decl_vendor->FindTypes(ConstString(name), /*max_matches*/ 1);
262
263 if (types.empty()) {
264 // The user can forward-declare something that has no definition. The
265 // runtime doesn't prohibit this at all. This is a rare and very weird
266 // case. Assert assert in debug builds so we catch other weird cases.
267 assert(false && "forward declaration without definition");
269 "forward declaration without definition: {0}", name);
270 return ast_ctx.getObjCIdType();
271 }
272
273 return ClangUtil::GetQualType(types.front().GetPointerType());
274 } else {
275 // We're going to resolve this dynamically anyway, so just smile and wave.
276 return ast_ctx.getObjCIdType();
277 }
278}
279
281 TypeSystemClang &clang_ast_ctx, llvm::StringRef &type, bool for_expression,
282 uint32_t *bitfield_bit_size) {
283 if (type.empty())
284 return clang::QualType();
285
286 clang::ASTContext &ast_ctx = clang_ast_ctx.getASTContext();
287
288 switch (type.front()) {
289 default:
290 break;
291 case _C_STRUCT_B:
292 return BuildStruct(clang_ast_ctx, type, for_expression);
293 case _C_ARY_B:
294 return BuildArray(clang_ast_ctx, type, for_expression);
295 case _C_UNION_B:
296 return BuildUnion(clang_ast_ctx, type, for_expression);
297 case _C_ID:
298 return BuildObjCObjectPointerType(clang_ast_ctx, type, for_expression);
299 }
300
301 // Save a copy for potential rollback.
302 llvm::StringRef backup = type;
303
304 switch (popChar(type)) {
305 default:
306 type = backup;
307 return clang::QualType();
308 case _C_CHR:
309 return ast_ctx.CharTy;
310 case _C_INT:
311 return ast_ctx.IntTy;
312 case _C_SHT:
313 return ast_ctx.ShortTy;
314 case _C_LNG:
315 return ast_ctx.getIntTypeForBitwidth(32, true);
316 // this used to be done like this:
317 // return clang_ast_ctx->GetIntTypeFromBitSize(32, true).GetQualType();
318 // which uses one of the constants if one is available, but we don't think
319 // all this work is necessary.
320 case _C_LNG_LNG:
321 return ast_ctx.LongLongTy;
322 case _C_UCHR:
323 return ast_ctx.UnsignedCharTy;
324 case _C_UINT:
325 return ast_ctx.UnsignedIntTy;
326 case _C_USHT:
327 return ast_ctx.UnsignedShortTy;
328 case _C_ULNG:
329 return ast_ctx.getIntTypeForBitwidth(32, false);
330 // see note for _C_LNG
331 case _C_ULNG_LNG:
332 return ast_ctx.UnsignedLongLongTy;
333 case _C_FLT:
334 return ast_ctx.FloatTy;
335 case _C_DBL:
336 return ast_ctx.DoubleTy;
337 case _C_BOOL:
338 return ast_ctx.BoolTy;
339 case _C_VOID:
340 return ast_ctx.VoidTy;
341 case _C_CHARPTR:
342 return ast_ctx.getPointerType(ast_ctx.CharTy);
343 case _C_CLASS:
344 return ast_ctx.getObjCClassType();
345 case _C_SEL:
346 return ast_ctx.getObjCSelType();
347 case _C_BFLD: {
348 uint32_t size = ReadNumber(type);
349 if (bitfield_bit_size) {
350 *bitfield_bit_size = size;
351 return ast_ctx.UnsignedIntTy; // FIXME: the spec is fairly vague here.
352 } else
353 return clang::QualType();
354 }
355 case _C_CONST: {
356 clang::QualType target_type =
357 BuildType(clang_ast_ctx, type, for_expression);
358 if (target_type.isNull())
359 return clang::QualType();
360 else if (target_type == ast_ctx.UnknownAnyTy)
361 return ast_ctx.UnknownAnyTy;
362 else
363 return ast_ctx.getConstType(target_type);
364 }
365 case _C_PTR: {
366 if (!for_expression && type.consume_front(_C_UNDEF)) {
367 // if we are not supporting the concept of unknownAny, but what is being
368 // created here is an unknownAny*, then we can just get away with a void*
369 // this is theoretically wrong (in the same sense as 'theoretically
370 // nothing exists') but is way better than outright failure in many
371 // practical cases
372 return ast_ctx.VoidPtrTy;
373 } else {
374 clang::QualType target_type =
375 BuildType(clang_ast_ctx, type, for_expression);
376 if (target_type.isNull())
377 return clang::QualType();
378 else if (target_type == ast_ctx.UnknownAnyTy)
379 return ast_ctx.UnknownAnyTy;
380 else
381 return ast_ctx.getPointerType(target_type);
382 }
383 }
384 case _C_UNDEF:
385 return for_expression ? ast_ctx.UnknownAnyTy : clang::QualType();
386 }
387}
388
390 const char *name,
391 bool for_expression) {
392 if (name && name[0]) {
393 llvm::StringRef lexer(name);
394 clang::QualType qual_type = BuildType(ast_ctx, lexer, for_expression);
395 return ast_ctx.GetType(qual_type);
396 }
397 return CompilerType();
398}
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