LLDB  mainline
AppleObjCTypeEncodingParser.cpp
Go to the documentation of this file.
1 //===-- AppleObjCTypeEncodingParser.cpp -------------------------*- C++ -*-===//
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 
12 #include "lldb/Symbol/ClangUtil.h"
14 #include "lldb/Target/Process.h"
15 #include "lldb/Target/Target.h"
17 
18 #include <vector>
19 
20 using namespace lldb_private;
21 using namespace lldb_utility;
22 
24  ObjCLanguageRuntime &runtime)
25  : ObjCLanguageRuntime::EncodingToType(), m_runtime(runtime) {
28  ->GetTarget()
30  .GetTriple()
31  .str()
32  .c_str()));
33 }
34 
35 std::string
36 AppleObjCTypeEncodingParser::ReadStructName(lldb_utility::StringLexer &type) {
37  StreamString buffer;
38  while (type.HasAtLeast(1) && type.Peek() != '=')
39  buffer.Printf("%c", type.Next());
40  return buffer.GetString();
41 }
42 
43 std::string
44 AppleObjCTypeEncodingParser::ReadQuotedString(lldb_utility::StringLexer &type) {
45  StreamString buffer;
46  while (type.HasAtLeast(1) && type.Peek() != '"')
47  buffer.Printf("%c", type.Next());
48  StringLexer::Character next = type.Next();
50  assert(next == '"');
51  return buffer.GetString();
52 }
53 
55 AppleObjCTypeEncodingParser::ReadNumber(lldb_utility::StringLexer &type) {
56  uint32_t total = 0;
57  while (type.HasAtLeast(1) && isdigit(type.Peek()))
58  total = 10 * total + (type.Next() - '0');
59  return total;
60 }
61 
62 // as an extension to the published grammar recent runtimes emit structs like
63 // this:
64 // "{CGRect=\"origin\"{CGPoint=\"x\"d\"y\"d}\"size\"{CGSize=\"width\"d\"height\"d}}"
65 
66 AppleObjCTypeEncodingParser::StructElement::StructElement()
67  : name(""), type(clang::QualType()), bitfield(0) {}
68 
69 AppleObjCTypeEncodingParser::StructElement
70 AppleObjCTypeEncodingParser::ReadStructElement(clang::ASTContext &ast_ctx,
72  bool for_expression) {
73  StructElement retval;
74  if (type.NextIf('"'))
75  retval.name = ReadQuotedString(type);
76  if (!type.NextIf('"'))
77  return retval;
78  uint32_t bitfield_size = 0;
79  retval.type = BuildType(ast_ctx, type, for_expression, &bitfield_size);
80  retval.bitfield = bitfield_size;
81  return retval;
82 }
83 
84 clang::QualType
85 AppleObjCTypeEncodingParser::BuildStruct(clang::ASTContext &ast_ctx,
87  bool for_expression) {
88  return BuildAggregate(ast_ctx, type, for_expression, '{', '}',
89  clang::TTK_Struct);
90 }
91 
92 clang::QualType
93 AppleObjCTypeEncodingParser::BuildUnion(clang::ASTContext &ast_ctx,
95  bool for_expression) {
96  return BuildAggregate(ast_ctx, type, for_expression, '(', ')',
97  clang::TTK_Union);
98 }
99 
100 clang::QualType AppleObjCTypeEncodingParser::BuildAggregate(
101  clang::ASTContext &ast_ctx, lldb_utility::StringLexer &type,
102  bool for_expression, char opener, char closer, uint32_t kind) {
103  if (!type.NextIf(opener))
104  return clang::QualType();
105  std::string name(ReadStructName(type));
106 
107  // We do not handle templated classes/structs at the moment. If the name has
108  // a < in it, we are going to abandon this. We're still obliged to parse it,
109  // so we just set a flag that means "Don't actually build anything."
110 
111  const bool is_templated = name.find('<') != std::string::npos;
112 
113  if (!type.NextIf('='))
114  return clang::QualType();
115  bool in_union = true;
116  std::vector<StructElement> elements;
117  while (in_union && type.HasAtLeast(1)) {
118  if (type.NextIf(closer)) {
119  in_union = false;
120  break;
121  } else {
122  auto element = ReadStructElement(ast_ctx, type, for_expression);
123  if (element.type.isNull())
124  break;
125  else
126  elements.push_back(element);
127  }
128  }
129  if (in_union)
130  return clang::QualType();
131 
132  if (is_templated)
133  return clang::QualType(); // This is where we bail out. Sorry!
134 
135  ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
136  if (!lldb_ctx)
137  return clang::QualType();
138  CompilerType union_type(lldb_ctx->CreateRecordType(
139  nullptr, lldb::eAccessPublic, name.c_str(), kind, lldb::eLanguageTypeC));
140  if (union_type) {
142 
143  unsigned int count = 0;
144  for (auto element : elements) {
145  if (element.name.empty()) {
146  StreamString elem_name;
147  elem_name.Printf("__unnamed_%u", count);
148  element.name = elem_name.GetString();
149  }
151  union_type, element.name.c_str(),
152  CompilerType(&ast_ctx, element.type), lldb::eAccessPublic,
153  element.bitfield);
154  ++count;
155  }
157  }
158  return ClangUtil::GetQualType(union_type);
159 }
160 
161 clang::QualType
162 AppleObjCTypeEncodingParser::BuildArray(clang::ASTContext &ast_ctx,
164  bool for_expression) {
165  if (!type.NextIf('['))
166  return clang::QualType();
167  uint32_t size = ReadNumber(type);
168  clang::QualType element_type(BuildType(ast_ctx, type, for_expression));
169  if (!type.NextIf(']'))
170  return clang::QualType();
171  ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
172  if (!lldb_ctx)
173  return clang::QualType();
174  CompilerType array_type(lldb_ctx->CreateArrayType(
175  CompilerType(&ast_ctx, element_type), size, false));
176  return ClangUtil::GetQualType(array_type);
177 }
178 
179 // the runtime can emit these in the form of @"SomeType", giving more specifics
180 // this would be interesting for expression parser interop, but since we
181 // actually try to avoid exposing the ivar info to the expression evaluator,
182 // consume but ignore the type info and always return an 'id'; if anything,
183 // dynamic typing will resolve things for us anyway
184 clang::QualType AppleObjCTypeEncodingParser::BuildObjCObjectPointerType(
185  clang::ASTContext &ast_ctx, lldb_utility::StringLexer &type,
186  bool for_expression) {
187  if (!type.NextIf('@'))
188  return clang::QualType();
189 
190  std::string name;
191 
192  if (type.NextIf('"')) {
193  // We have to be careful here. We're used to seeing
194  // @"NSString"
195  // but in records it is possible that the string following an @ is the name
196  // of the next field and @ means "id". This is the case if anything
197  // unquoted except for "}", the end of the type, or another name follows
198  // the quoted string.
199  //
200  // E.g.
201  // - @"NSString"@ means "id, followed by a field named NSString of type id"
202  // - @"NSString"} means "a pointer to NSString and the end of the struct" -
203  // @"NSString""nextField" means "a pointer to NSString and a field named
204  // nextField" - @"NSString" followed by the end of the string means "a
205  // pointer to NSString"
206  //
207  // As a result, the rule is: If we see @ followed by a quoted string, we
208  // peek. - If we see }, ), ], the end of the string, or a quote ("), the
209  // quoted string is a class name. - If we see anything else, the quoted
210  // string is a field name and we push it back onto type.
211 
212  name = ReadQuotedString(type);
213 
214  if (type.HasAtLeast(1)) {
215  switch (type.Peek()) {
216  default:
217  // roll back
218  type.PutBack(name.length() +
219  2); // undo our consumption of the string and of the quotes
220  name.clear();
221  break;
222  case '}':
223  case ')':
224  case ']':
225  case '"':
226  // the quoted string is a class name – see the rule
227  break;
228  }
229  } else {
230  // the quoted string is a class name – see the rule
231  }
232  }
233 
234  if (for_expression && !name.empty()) {
235  size_t less_than_pos = name.find('<');
236 
237  if (less_than_pos != std::string::npos) {
238  if (less_than_pos == 0)
239  return ast_ctx.getObjCIdType();
240  else
241  name.erase(less_than_pos);
242  }
243 
244  DeclVendor *decl_vendor = m_runtime.GetDeclVendor();
245  if (!decl_vendor)
246  return clang::QualType();
247 
248  const bool append = false;
249  const uint32_t max_matches = 1;
250  std::vector<clang::NamedDecl *> decls;
251 
252  uint32_t num_types =
253  decl_vendor->FindDecls(ConstString(name), append, max_matches, decls);
254 
255 // The user can forward-declare something that has no definition. The runtime
256 // doesn't prohibit this at all. This is a rare and very weird case. We keep
257 // this assert in debug builds so we catch other weird cases.
258 #ifdef LLDB_CONFIGURATION_DEBUG
259  assert(num_types);
260 #else
261  if (!num_types)
262  return ast_ctx.getObjCIdType();
263 #endif
264 
265  return ClangUtil::GetQualType(
266  ClangASTContext::GetTypeForDecl(decls[0]).GetPointerType());
267  } else {
268  // We're going to resolve this dynamically anyway, so just smile and wave.
269  return ast_ctx.getObjCIdType();
270  }
271 }
272 
273 clang::QualType
274 AppleObjCTypeEncodingParser::BuildType(clang::ASTContext &ast_ctx,
275  StringLexer &type, bool for_expression,
276  uint32_t *bitfield_bit_size) {
277  if (!type.HasAtLeast(1))
278  return clang::QualType();
279 
280  switch (type.Peek()) {
281  default:
282  break;
283  case '{':
284  return BuildStruct(ast_ctx, type, for_expression);
285  case '[':
286  return BuildArray(ast_ctx, type, for_expression);
287  case '(':
288  return BuildUnion(ast_ctx, type, for_expression);
289  case '@':
290  return BuildObjCObjectPointerType(ast_ctx, type, for_expression);
291  }
292 
293  switch (type.Next()) {
294  default:
295  type.PutBack(1);
296  return clang::QualType();
297  case 'c':
298  return ast_ctx.CharTy;
299  case 'i':
300  return ast_ctx.IntTy;
301  case 's':
302  return ast_ctx.ShortTy;
303  case 'l':
304  return ast_ctx.getIntTypeForBitwidth(32, true);
305  // this used to be done like this:
306  // ClangASTContext *lldb_ctx = ClangASTContext::GetASTContext(&ast_ctx);
307  // if (!lldb_ctx)
308  // return clang::QualType();
309  // return lldb_ctx->GetIntTypeFromBitSize(32, true).GetQualType();
310  // which uses one of the constants if one is available, but we don't think
311  // all this work is necessary.
312  case 'q':
313  return ast_ctx.LongLongTy;
314  case 'C':
315  return ast_ctx.UnsignedCharTy;
316  case 'I':
317  return ast_ctx.UnsignedIntTy;
318  case 'S':
319  return ast_ctx.UnsignedShortTy;
320  case 'L':
321  return ast_ctx.getIntTypeForBitwidth(32, false);
322  // see note for 'l'
323  case 'Q':
324  return ast_ctx.UnsignedLongLongTy;
325  case 'f':
326  return ast_ctx.FloatTy;
327  case 'd':
328  return ast_ctx.DoubleTy;
329  case 'B':
330  return ast_ctx.BoolTy;
331  case 'v':
332  return ast_ctx.VoidTy;
333  case '*':
334  return ast_ctx.getPointerType(ast_ctx.CharTy);
335  case '#':
336  return ast_ctx.getObjCClassType();
337  case ':':
338  return ast_ctx.getObjCSelType();
339  case 'b': {
340  uint32_t size = ReadNumber(type);
341  if (bitfield_bit_size) {
342  *bitfield_bit_size = size;
343  return ast_ctx.UnsignedIntTy; // FIXME: the spec is fairly vague here.
344  } else
345  return clang::QualType();
346  }
347  case 'r': {
348  clang::QualType target_type = BuildType(ast_ctx, type, for_expression);
349  if (target_type.isNull())
350  return clang::QualType();
351  else if (target_type == ast_ctx.UnknownAnyTy)
352  return ast_ctx.UnknownAnyTy;
353  else
354  return ast_ctx.getConstType(target_type);
355  }
356  case '^': {
357  if (!for_expression && type.NextIf('?')) {
358  // if we are not supporting the concept of unknownAny, but what is being
359  // created here is an unknownAny*, then we can just get away with a void*
360  // this is theoretically wrong (in the same sense as 'theoretically
361  // nothing exists') but is way better than outright failure in many
362  // practical cases
363  return ast_ctx.VoidPtrTy;
364  } else {
365  clang::QualType target_type = BuildType(ast_ctx, type, for_expression);
366  if (target_type.isNull())
367  return clang::QualType();
368  else if (target_type == ast_ctx.UnknownAnyTy)
369  return ast_ctx.UnknownAnyTy;
370  else
371  return ast_ctx.getPointerType(target_type);
372  }
373  }
374  case '?':
375  return for_expression ? ast_ctx.UnknownAnyTy : clang::QualType();
376  }
377 }
378 
380  clang::ASTContext &ast_ctx, const char *name, bool for_expression) {
381  if (name && name[0]) {
382  StringLexer lexer(name);
383  clang::QualType qual_type = BuildType(ast_ctx, lexer, for_expression);
384  return CompilerType(&ast_ctx, qual_type);
385  }
386  return CompilerType();
387 }
static bool CompleteTagDeclarationDefinition(const CompilerType &type)
CompilerType RealizeType(clang::ASTContext &ast_ctx, const char *name, bool for_expression) override
CompilerType CreateArrayType(const CompilerType &element_type, size_t element_count, bool is_vector)
#define UNUSED_IF_ASSERT_DISABLED(x)
Definition: lldb-defines.h:134
Enumerations for broadcasting.
Definition: SBLaunchInfo.h:14
const ArchSpec & GetArchitecture() const
Definition: Target.h:941
static CompilerType GetTypeForDecl(clang::NamedDecl *decl)
std::string::value_type Character
Definition: StringLexer.h:23
static ClangASTContext * GetASTContext(clang::ASTContext *ast_ctx)
llvm::Triple & GetTriple()
Architecture triple accessor.
Definition: ArchSpec.h:431
CompilerType CreateRecordType(clang::DeclContext *decl_ctx, lldb::AccessType access_type, const char *name, int kind, lldb::LanguageType language, ClangASTMetadata *metadata=nullptr)
std::unique_ptr< ClangASTContext > m_scratch_ast_ctx_up
static bool StartTagDeclarationDefinition(const CompilerType &type)
llvm::StringRef GetString() const
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition: Stream.cpp:106
static clang::FieldDecl * AddFieldToRecordType(const CompilerType &type, llvm::StringRef name, const CompilerType &field_type, lldb::AccessType access, uint32_t bitfield_bit_size)
virtual uint32_t FindDecls(ConstString name, bool append, uint32_t max_matches, std::vector< clang::NamedDecl *> &decls)=0
Look up the set of Decls that the DeclVendor currently knows about matching a given name...
Target & GetTarget()
Get the target object pointer for this module.
Definition: Process.h:1194
A uniqued constant string class.
Definition: ConstString.h:38
bool NextIf(Character c)
Definition: StringLexer.cpp:23
Non-standardized C, such as K&R.
static clang::QualType GetQualType(const CompilerType &ct)
Definition: ClangUtil.cpp:27