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