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