Coverage Report

Created: 2022-07-16 07:03

/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
}