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