Coverage Report

Created: 2023-09-30 09:22

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