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