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