Coverage Report

Created: 2022-01-18 06:27

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- ClangExpressionSourceCode.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 "ClangExpressionSourceCode.h"
10
11
#include "clang/Basic/CharInfo.h"
12
#include "clang/Basic/FileManager.h"
13
#include "clang/Basic/SourceManager.h"
14
#include "clang/Lex/Lexer.h"
15
#include "llvm/ADT/StringRef.h"
16
17
#include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h"
18
#include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h"
19
#include "lldb/Symbol/Block.h"
20
#include "lldb/Symbol/CompileUnit.h"
21
#include "lldb/Symbol/DebugMacros.h"
22
#include "lldb/Symbol/TypeSystem.h"
23
#include "lldb/Symbol/VariableList.h"
24
#include "lldb/Target/ExecutionContext.h"
25
#include "lldb/Target/Language.h"
26
#include "lldb/Target/Platform.h"
27
#include "lldb/Target/StackFrame.h"
28
#include "lldb/Target/Target.h"
29
#include "lldb/Utility/StreamString.h"
30
31
using namespace lldb_private;
32
33
#define PREFIX_NAME "<lldb wrapper prefix>"
34
#define SUFFIX_NAME "<lldb wrapper suffix>"
35
36
const llvm::StringRef ClangExpressionSourceCode::g_prefix_file_name = PREFIX_NAME;
37
38
const char *ClangExpressionSourceCode::g_expression_prefix =
39
"#line 1 \"" PREFIX_NAME R"("
40
#ifndef offsetof
41
#define offsetof(t, d) __builtin_offsetof(t, d)
42
#endif
43
#ifndef NULL
44
#define NULL (__null)
45
#endif
46
#ifndef Nil
47
#define Nil (__null)
48
#endif
49
#ifndef nil
50
#define nil (__null)
51
#endif
52
#ifndef YES
53
#define YES ((BOOL)1)
54
#endif
55
#ifndef NO
56
#define NO ((BOOL)0)
57
#endif
58
typedef __INT8_TYPE__ int8_t;
59
typedef __UINT8_TYPE__ uint8_t;
60
typedef __INT16_TYPE__ int16_t;
61
typedef __UINT16_TYPE__ uint16_t;
62
typedef __INT32_TYPE__ int32_t;
63
typedef __UINT32_TYPE__ uint32_t;
64
typedef __INT64_TYPE__ int64_t;
65
typedef __UINT64_TYPE__ uint64_t;
66
typedef __INTPTR_TYPE__ intptr_t;
67
typedef __UINTPTR_TYPE__ uintptr_t;
68
typedef __SIZE_TYPE__ size_t;
69
typedef __PTRDIFF_TYPE__ ptrdiff_t;
70
typedef unsigned short unichar;
71
extern "C"
72
{
73
    int printf(const char * __restrict, ...);
74
}
75
)";
76
77
const char *ClangExpressionSourceCode::g_expression_suffix =
78
    "\n;\n#line 1 \"" SUFFIX_NAME "\"\n";
79
80
namespace {
81
82
class AddMacroState {
83
  enum State {
84
    CURRENT_FILE_NOT_YET_PUSHED,
85
    CURRENT_FILE_PUSHED,
86
    CURRENT_FILE_POPPED
87
  };
88
89
public:
90
  AddMacroState(const FileSpec &current_file, const uint32_t current_file_line)
91
      : m_state(CURRENT_FILE_NOT_YET_PUSHED), m_current_file(current_file),
92
0
        m_current_file_line(current_file_line) {}
93
94
0
  void StartFile(const FileSpec &file) {
95
0
    m_file_stack.push_back(file);
96
0
    if (file == m_current_file)
97
0
      m_state = CURRENT_FILE_PUSHED;
98
0
  }
99
100
0
  void EndFile() {
101
0
    if (m_file_stack.size() == 0)
102
0
      return;
103
104
0
    FileSpec old_top = m_file_stack.back();
105
0
    m_file_stack.pop_back();
106
0
    if (old_top == m_current_file)
107
0
      m_state = CURRENT_FILE_POPPED;
108
0
  }
109
110
  // An entry is valid if it occurs before the current line in the current
111
  // file.
112
0
  bool IsValidEntry(uint32_t line) {
113
0
    switch (m_state) {
114
0
    case CURRENT_FILE_NOT_YET_PUSHED:
115
0
      return true;
116
0
    case CURRENT_FILE_PUSHED:
117
      // If we are in file included in the current file, the entry should be
118
      // added.
119
0
      if (m_file_stack.back() != m_current_file)
120
0
        return true;
121
122
0
      return line < m_current_file_line;
123
0
    default:
124
0
      return false;
125
0
    }
126
0
  }
127
128
private:
129
  std::vector<FileSpec> m_file_stack;
130
  State m_state;
131
  FileSpec m_current_file;
132
  uint32_t m_current_file_line;
133
};
134
135
} // anonymous namespace
136
137
static void AddMacros(const DebugMacros *dm, CompileUnit *comp_unit,
138
0
                      AddMacroState &state, StreamString &stream) {
139
0
  if (dm == nullptr)
140
0
    return;
141
142
0
  for (size_t i = 0; i < dm->GetNumMacroEntries(); i++) {
143
0
    const DebugMacroEntry &entry = dm->GetMacroEntryAtIndex(i);
144
0
    uint32_t line;
145
146
0
    switch (entry.GetType()) {
147
0
    case DebugMacroEntry::DEFINE:
148
0
      if (state.IsValidEntry(entry.GetLineNumber()))
149
0
        stream.Printf("#define %s\n", entry.GetMacroString().AsCString());
150
0
      else
151
0
        return;
152
0
      break;
153
0
    case DebugMacroEntry::UNDEF:
154
0
      if (state.IsValidEntry(entry.GetLineNumber()))
155
0
        stream.Printf("#undef %s\n", entry.GetMacroString().AsCString());
156
0
      else
157
0
        return;
158
0
      break;
159
0
    case DebugMacroEntry::START_FILE:
160
0
      line = entry.GetLineNumber();
161
0
      if (state.IsValidEntry(line))
162
0
        state.StartFile(entry.GetFileSpec(comp_unit));
163
0
      else
164
0
        return;
165
0
      break;
166
0
    case DebugMacroEntry::END_FILE:
167
0
      state.EndFile();
168
0
      break;
169
0
    case DebugMacroEntry::INDIRECT:
170
0
      AddMacros(entry.GetIndirectDebugMacros(), comp_unit, state, stream);
171
0
      break;
172
0
    default:
173
      // This is an unknown/invalid entry. Ignore.
174
0
      break;
175
0
    }
176
0
  }
177
0
}
178
179
lldb_private::ClangExpressionSourceCode::ClangExpressionSourceCode(
180
    llvm::StringRef filename, llvm::StringRef name, llvm::StringRef prefix,
181
    llvm::StringRef body, Wrapping wrap, WrapKind wrap_kind)
182
9.27k
    : ExpressionSourceCode(name, prefix, body, wrap), m_wrap_kind(wrap_kind) {
183
  // Use #line markers to pretend that we have a single-line source file
184
  // containing only the user expression. This will hide our wrapper code
185
  // from the user when we render diagnostics with Clang.
186
9.27k
  m_start_marker = "#line 1 \"" + filename.str() + "\"\n";
187
9.27k
  m_end_marker = g_expression_suffix;
188
9.27k
}
189
190
namespace {
191
/// Allows checking if a token is contained in a given expression.
192
class TokenVerifier {
193
  /// The tokens we found in the expression.
194
  llvm::StringSet<> m_tokens;
195
196
public:
197
  TokenVerifier(std::string body);
198
  /// Returns true iff the given expression body contained a token with the
199
  /// given content.
200
99.5k
  bool hasToken(llvm::StringRef token) const {
201
99.5k
    return m_tokens.find(token) != m_tokens.end();
202
99.5k
  }
203
};
204
} // namespace
205
206
8.84k
TokenVerifier::TokenVerifier(std::string body) {
207
8.84k
  using namespace clang;
208
209
  // We only care about tokens and not their original source locations. If we
210
  // move the whole expression to only be in one line we can simplify the
211
  // following code that extracts the token contents.
212
8.84k
  std::replace(body.begin(), body.end(), '\n', ' ');
213
8.84k
  std::replace(body.begin(), body.end(), '\r', ' ');
214
215
8.84k
  FileSystemOptions file_opts;
216
8.84k
  FileManager file_mgr(file_opts,
217
8.84k
                       FileSystem::Instance().GetVirtualFileSystem());
218
219
  // Let's build the actual source code Clang needs and setup some utility
220
  // objects.
221
8.84k
  llvm::IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs());
222
8.84k
  llvm::IntrusiveRefCntPtr<DiagnosticOptions> diags_opts(
223
8.84k
      new DiagnosticOptions());
224
8.84k
  DiagnosticsEngine diags(diag_ids, diags_opts);
225
8.84k
  clang::SourceManager SM(diags, file_mgr);
226
8.84k
  auto buf = llvm::MemoryBuffer::getMemBuffer(body);
227
228
8.84k
  FileID FID = SM.createFileID(buf->getMemBufferRef());
229
230
  // Let's just enable the latest ObjC and C++ which should get most tokens
231
  // right.
232
8.84k
  LangOptions Opts;
233
8.84k
  Opts.ObjC = true;
234
8.84k
  Opts.DollarIdents = true;
235
8.84k
  Opts.CPlusPlus17 = true;
236
8.84k
  Opts.LineComment = true;
237
238
8.84k
  Lexer lex(FID, buf->getMemBufferRef(), SM, Opts);
239
240
8.84k
  Token token;
241
8.84k
  bool exit = false;
242
67.6k
  while (!exit) {
243
    // Returns true if this is the last token we get from the lexer.
244
58.7k
    exit = lex.LexFromRawLexer(token);
245
246
    // Extract the column number which we need to extract the token content.
247
    // Our expression is just one line, so we don't need to handle any line
248
    // numbers here.
249
58.7k
    bool invalid = false;
250
58.7k
    unsigned start = SM.getSpellingColumnNumber(token.getLocation(), &invalid);
251
58.7k
    if (invalid)
252
0
      continue;
253
    // Column numbers start at 1, but indexes in our string start at 0.
254
58.7k
    --start;
255
256
    // Annotations don't have a length, so let's skip them.
257
58.7k
    if (token.isAnnotation())
258
0
      continue;
259
260
    // Extract the token string from our source code and store it.
261
58.7k
    std::string token_str = body.substr(start, token.getLength());
262
58.7k
    if (token_str.empty())
263
84
      continue;
264
58.6k
    m_tokens.insert(token_str);
265
58.6k
  }
266
8.84k
}
267
268
void ClangExpressionSourceCode::AddLocalVariableDecls(
269
    const lldb::VariableListSP &var_list_sp, StreamString &stream,
270
8.84k
    const std::string &expr) const {
271
8.84k
  TokenVerifier tokens(expr);
272
273
110k
  for (size_t i = 0; i < var_list_sp->GetSize(); 
i++101k
) {
274
101k
    lldb::VariableSP var_sp = var_list_sp->GetVariableAtIndex(i);
275
276
101k
    ConstString var_name = var_sp->GetName();
277
278
279
    // We can check for .block_descriptor w/o checking for langauge since this
280
    // is not a valid identifier in either C or C++.
281
101k
    if (!var_name || var_name == ".block_descriptor")
282
1.53k
      continue;
283
284
99.9k
    if (!expr.empty() && 
!tokens.hasToken(var_name.GetStringRef())99.5k
)
285
90.8k
      continue;
286
287
9.06k
    const bool is_objc = m_wrap_kind == WrapKind::ObjCInstanceMethod ||
288
9.06k
                         
m_wrap_kind == WrapKind::ObjCStaticMethod9.01k
;
289
9.06k
    if ((var_name == "self" || 
var_name == "_cmd"9.00k
) &&
is_objc62
)
290
62
      continue;
291
292
9.00k
    if (var_name == "this" && 
m_wrap_kind == WrapKind::CppMemberFunction35
)
293
33
      continue;
294
295
8.97k
    stream.Printf("using $__lldb_local_vars::%s;\n", var_name.AsCString());
296
8.97k
  }
297
8.84k
}
298
299
bool ClangExpressionSourceCode::GetText(
300
    std::string &text, ExecutionContext &exe_ctx, bool add_locals,
301
9.27k
    bool force_add_all_locals, llvm::ArrayRef<std::string> modules) const {
302
9.27k
  const char *target_specific_defines = "typedef signed char BOOL;\n";
303
9.27k
  std::string module_macros;
304
9.27k
  llvm::raw_string_ostream module_macros_stream(module_macros);
305
306
9.27k
  Target *target = exe_ctx.GetTargetPtr();
307
9.27k
  if (target) {
308
9.27k
    if (target->GetArchitecture().GetMachine() == llvm::Triple::aarch64 ||
309
9.27k
        target->GetArchitecture().GetMachine() == llvm::Triple::aarch64_32) {
310
0
      target_specific_defines = "typedef bool BOOL;\n";
311
0
    }
312
9.27k
    if (target->GetArchitecture().GetMachine() == llvm::Triple::x86_64) {
313
9.27k
      if (lldb::PlatformSP platform_sp = target->GetPlatform()) {
314
9.27k
        if (platform_sp->GetPluginName() == "ios-simulator") {
315
0
          target_specific_defines = "typedef bool BOOL;\n";
316
0
        }
317
9.27k
      }
318
9.27k
    }
319
320
9.27k
    auto *persistent_vars = llvm::cast<ClangPersistentVariables>(
321
9.27k
        target->GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC));
322
9.27k
    std::shared_ptr<ClangModulesDeclVendor> decl_vendor =
323
9.27k
        persistent_vars->GetClangModulesDeclVendor();
324
9.27k
    if (decl_vendor) {
325
9.26k
      const ClangModulesDeclVendor::ModuleVector &hand_imported_modules =
326
9.26k
          persistent_vars->GetHandLoadedClangModules();
327
9.26k
      ClangModulesDeclVendor::ModuleVector modules_for_macros;
328
329
31.9k
      for (ClangModulesDeclVendor::ModuleID module : hand_imported_modules) {
330
31.9k
        modules_for_macros.push_back(module);
331
31.9k
      }
332
333
9.26k
      if (target->GetEnableAutoImportClangModules()) {
334
9.26k
        if (StackFrame *frame = exe_ctx.GetFramePtr()) {
335
8.91k
          if (Block *block = frame->GetFrameBlock()) {
336
8.88k
            SymbolContext sc;
337
338
8.88k
            block->CalculateSymbolContext(&sc);
339
340
8.88k
            if (sc.comp_unit) {
341
8.88k
              StreamString error_stream;
342
343
8.88k
              decl_vendor->AddModulesForCompileUnit(
344
8.88k
                  *sc.comp_unit, modules_for_macros, error_stream);
345
8.88k
            }
346
8.88k
          }
347
8.91k
        }
348
9.26k
      }
349
350
9.26k
      decl_vendor->ForEachMacro(
351
9.26k
          modules_for_macros,
352
9.26k
          [&module_macros_stream](llvm::StringRef token,
353
3.70M
                                  llvm::StringRef expansion) -> bool {
354
            // Check if the macro hasn't already been defined in the
355
            // g_expression_prefix (which defines a few builtin macros).
356
3.70M
            module_macros_stream << "#ifndef " << token << "\n";
357
3.70M
            module_macros_stream << expansion << "\n";
358
3.70M
            module_macros_stream << "#endif\n";
359
3.70M
            return false;
360
3.70M
          });
361
9.26k
    }
362
9.27k
  }
363
364
9.27k
  StreamString debug_macros_stream;
365
9.27k
  StreamString lldb_local_var_decls;
366
9.27k
  if (StackFrame *frame = exe_ctx.GetFramePtr()) {
367
8.91k
    const SymbolContext &sc = frame->GetSymbolContext(
368
8.91k
        lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry);
369
370
8.91k
    if (sc.comp_unit && 
sc.line_entry.IsValid()8.88k
) {
371
8.88k
      DebugMacros *dm = sc.comp_unit->GetDebugMacros();
372
8.88k
      if (dm) {
373
0
        AddMacroState state(sc.line_entry.file, sc.line_entry.line);
374
0
        AddMacros(dm, sc.comp_unit, state, debug_macros_stream);
375
0
      }
376
8.88k
    }
377
378
8.91k
    if (add_locals)
379
8.87k
      if (target->GetInjectLocalVariables(&exe_ctx)) {
380
8.84k
        lldb::VariableListSP var_list_sp =
381
8.84k
            frame->GetInScopeVariableList(false, true);
382
8.84k
        AddLocalVariableDecls(var_list_sp, lldb_local_var_decls,
383
8.84k
                              force_add_all_locals ? 
""74
:
m_body8.77k
);
384
8.84k
      }
385
8.91k
  }
386
387
9.27k
  if (m_wrap) {
388
    // Generate a list of @import statements that will import the specified
389
    // module into our expression.
390
9.27k
    std::string module_imports;
391
9.27k
    for (const std::string &module : modules) {
392
789
      module_imports.append("@import ");
393
789
      module_imports.append(module);
394
789
      module_imports.append(";\n");
395
789
    }
396
397
9.27k
    StreamString wrap_stream;
398
399
9.27k
    wrap_stream.Printf("%s\n%s\n%s\n%s\n%s\n", g_expression_prefix,
400
9.27k
                       module_macros.c_str(), debug_macros_stream.GetData(),
401
9.27k
                       target_specific_defines, m_prefix.c_str());
402
403
    // First construct a tagged form of the user expression so we can find it
404
    // later:
405
9.27k
    std::string tagged_body;
406
9.27k
    tagged_body.append(m_start_marker);
407
9.27k
    tagged_body.append(m_body);
408
9.27k
    tagged_body.append(m_end_marker);
409
410
9.27k
    switch (m_wrap_kind) {
411
9.01k
    case WrapKind::Function:
412
9.01k
      wrap_stream.Printf("%s"
413
9.01k
                         "void                           \n"
414
9.01k
                         "%s(void *$__lldb_arg)          \n"
415
9.01k
                         "{                              \n"
416
9.01k
                         "    %s;                        \n"
417
9.01k
                         "%s"
418
9.01k
                         "}                              \n",
419
9.01k
                         module_imports.c_str(), m_name.c_str(),
420
9.01k
                         lldb_local_var_decls.GetData(), tagged_body.c_str());
421
9.01k
      break;
422
143
    case WrapKind::CppMemberFunction:
423
143
      wrap_stream.Printf("%s"
424
143
                         "void                                   \n"
425
143
                         "$__lldb_class::%s(void *$__lldb_arg)   \n"
426
143
                         "{                                      \n"
427
143
                         "    %s;                                \n"
428
143
                         "%s"
429
143
                         "}                                      \n",
430
143
                         module_imports.c_str(), m_name.c_str(),
431
143
                         lldb_local_var_decls.GetData(), tagged_body.c_str());
432
143
      break;
433
101
    case WrapKind::ObjCInstanceMethod:
434
101
      wrap_stream.Printf(
435
101
          "%s"
436
101
          "@interface $__lldb_objc_class ($__lldb_category)       \n"
437
101
          "-(void)%s:(void *)$__lldb_arg;                         \n"
438
101
          "@end                                                   \n"
439
101
          "@implementation $__lldb_objc_class ($__lldb_category)  \n"
440
101
          "-(void)%s:(void *)$__lldb_arg                          \n"
441
101
          "{                                                      \n"
442
101
          "    %s;                                                \n"
443
101
          "%s"
444
101
          "}                                                      \n"
445
101
          "@end                                                   \n",
446
101
          module_imports.c_str(), m_name.c_str(), m_name.c_str(),
447
101
          lldb_local_var_decls.GetData(), tagged_body.c_str());
448
101
      break;
449
450
15
    case WrapKind::ObjCStaticMethod:
451
15
      wrap_stream.Printf(
452
15
          "%s"
453
15
          "@interface $__lldb_objc_class ($__lldb_category)        \n"
454
15
          "+(void)%s:(void *)$__lldb_arg;                          \n"
455
15
          "@end                                                    \n"
456
15
          "@implementation $__lldb_objc_class ($__lldb_category)   \n"
457
15
          "+(void)%s:(void *)$__lldb_arg                           \n"
458
15
          "{                                                       \n"
459
15
          "    %s;                                                 \n"
460
15
          "%s"
461
15
          "}                                                       \n"
462
15
          "@end                                                    \n",
463
15
          module_imports.c_str(), m_name.c_str(), m_name.c_str(),
464
15
          lldb_local_var_decls.GetData(), tagged_body.c_str());
465
15
      break;
466
9.27k
    }
467
468
9.27k
    text = std::string(wrap_stream.GetString());
469
9.27k
  } else {
470
0
    text.append(m_body);
471
0
  }
472
473
9.27k
  return true;
474
9.27k
}
475
476
bool ClangExpressionSourceCode::GetOriginalBodyBounds(
477
9.29k
    std::string transformed_text, size_t &start_loc, size_t &end_loc) {
478
9.29k
  start_loc = transformed_text.find(m_start_marker);
479
9.29k
  if (start_loc == std::string::npos)
480
0
    return false;
481
9.29k
  start_loc += m_start_marker.size();
482
9.29k
  end_loc = transformed_text.find(m_end_marker);
483
9.29k
  return end_loc != std::string::npos;
484
9.29k
}