1 //===-- ClangExpressionSourceCode.cpp ---------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "ClangExpressionSourceCode.h"
11 #include "clang/Basic/CharInfo.h"
12 #include "clang/Basic/SourceManager.h"
13 #include "clang/Lex/Lexer.h"
14 #include "llvm/ADT/StringRef.h"
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"
30 using namespace lldb_private;
32 const char *ClangExpressionSourceCode::g_expression_prefix = R"(
48 typedef __INT8_TYPE__ int8_t;
49 typedef __UINT8_TYPE__ uint8_t;
50 typedef __INT16_TYPE__ int16_t;
51 typedef __UINT16_TYPE__ uint16_t;
52 typedef __INT32_TYPE__ int32_t;
53 typedef __UINT32_TYPE__ uint32_t;
54 typedef __INT64_TYPE__ int64_t;
55 typedef __UINT64_TYPE__ uint64_t;
56 typedef __INTPTR_TYPE__ intptr_t;
57 typedef __UINTPTR_TYPE__ uintptr_t;
58 typedef __SIZE_TYPE__ size_t;
59 typedef __PTRDIFF_TYPE__ ptrdiff_t;
60 typedef unsigned short unichar;
63 int printf(const char * __restrict, ...);
67 static const char *c_start_marker = " /*LLDB_BODY_START*/\n ";
68 static const char *c_end_marker = ";\n /*LLDB_BODY_END*/\n";
74 CURRENT_FILE_NOT_YET_PUSHED,
80 AddMacroState(const FileSpec ¤t_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) {}
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;
91 if (m_file_stack.size() == 0)
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;
100 // An entry is valid if it occurs before the current line in the current
102 bool IsValidEntry(uint32_t line) {
104 case CURRENT_FILE_NOT_YET_PUSHED:
106 case CURRENT_FILE_PUSHED:
107 // If we are in file included in the current file, the entry should be
109 if (m_file_stack.back() != m_current_file)
112 return line < m_current_file_line;
119 std::vector<FileSpec> m_file_stack;
121 FileSpec m_current_file;
122 uint32_t m_current_file_line;
125 } // anonymous namespace
127 static void AddMacros(const DebugMacros *dm, CompileUnit *comp_unit,
128 AddMacroState &state, StreamString &stream) {
132 for (size_t i = 0; i < dm->GetNumMacroEntries(); i++) {
133 const DebugMacroEntry &entry = dm->GetMacroEntryAtIndex(i);
136 switch (entry.GetType()) {
137 case DebugMacroEntry::DEFINE:
138 if (state.IsValidEntry(entry.GetLineNumber()))
139 stream.Printf("#define %s\n", entry.GetMacroString().AsCString());
143 case DebugMacroEntry::UNDEF:
144 if (state.IsValidEntry(entry.GetLineNumber()))
145 stream.Printf("#undef %s\n", entry.GetMacroString().AsCString());
149 case DebugMacroEntry::START_FILE:
150 line = entry.GetLineNumber();
151 if (state.IsValidEntry(line))
152 state.StartFile(entry.GetFileSpec(comp_unit));
156 case DebugMacroEntry::END_FILE:
159 case DebugMacroEntry::INDIRECT:
160 AddMacros(entry.GetIndirectDebugMacros(), comp_unit, state, stream);
163 // This is an unknown/invalid entry. Ignore.
170 /// Allows checking if a token is contained in a given expression.
171 class TokenVerifier {
172 /// The tokens we found in the expression.
173 llvm::StringSet<> m_tokens;
176 TokenVerifier(std::string body);
177 /// Returns true iff the given expression body contained a token with the
179 bool hasToken(llvm::StringRef token) const {
180 return m_tokens.find(token) != m_tokens.end();
185 TokenVerifier::TokenVerifier(std::string body) {
186 using namespace clang;
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', ' ');
194 FileSystemOptions file_opts;
195 FileManager file_mgr(file_opts,
196 FileSystem::Instance().GetVirtualFileSystem());
198 // Let's build the actual source code Clang needs and setup some utility
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);
207 FileID FID = SM.createFileID(clang::SourceManager::Unowned, buf.get());
209 // Let's just enable the latest ObjC and C++ which should get most tokens
213 Opts.DollarIdents = true;
214 Opts.CPlusPlus17 = true;
215 Opts.LineComment = true;
217 Lexer lex(FID, buf.get(), SM, Opts);
222 // Returns true if this is the last token we get from the lexer.
223 exit = lex.LexFromRawLexer(token);
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
228 bool invalid = false;
229 unsigned start = SM.getSpellingColumnNumber(token.getLocation(), &invalid);
232 // Column numbers start at 1, but indexes in our string start at 0.
235 // Annotations don't have a length, so let's skip them.
236 if (token.isAnnotation())
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())
243 m_tokens.insert(token_str);
247 static 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);
253 for (size_t i = 0; i < var_list_sp->GetSize(); i++) {
254 lldb::VariableSP var_sp = var_list_sp->GetVariableAtIndex(i);
256 ConstString var_name = var_sp->GetName();
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")
264 if (!expr.empty() && !tokens.hasToken(var_name.GetStringRef()))
267 if ((var_name == "self" || var_name == "_cmd") &&
268 (wrapping_language == lldb::eLanguageTypeObjC ||
269 wrapping_language == lldb::eLanguageTypeObjC_plus_plus))
272 if (var_name == "this" &&
273 wrapping_language == lldb::eLanguageTypeC_plus_plus)
276 stream.Printf("using $__lldb_local_vars::%s;\n", var_name.AsCString());
280 bool 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;
287 Target *target = exe_ctx.GetTargetPtr();
289 if (target->GetArchitecture().GetMachine() == llvm::Triple::aarch64) {
290 target_specific_defines = "typedef bool BOOL;\n";
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";
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;
311 for (ClangModulesDeclVendor::ModuleID module : hand_imported_modules) {
312 modules_for_macros.push_back(module);
315 if (target->GetEnableAutoImportClangModules()) {
316 if (StackFrame *frame = exe_ctx.GetFramePtr()) {
317 if (Block *block = frame->GetFrameBlock()) {
320 block->CalculateSymbolContext(&sc);
323 StreamString error_stream;
325 decl_vendor->AddModulesForCompileUnit(
326 *sc.comp_unit, modules_for_macros, error_stream);
332 decl_vendor->ForEachMacro(
334 [&module_macros](const std::string &expansion) -> bool {
335 module_macros.append(expansion);
336 module_macros.append("\n");
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);
348 if (sc.comp_unit && sc.line_entry.IsValid()) {
349 DebugMacros *dm = sc.comp_unit->GetDebugMacros();
351 AddMacroState state(sc.line_entry.file, sc.line_entry.line);
352 AddMacros(dm, sc.comp_unit, state, debug_macros_stream);
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,
367 switch (wrapping_language) {
370 case lldb::eLanguageTypeC:
371 case lldb::eLanguageTypeC_plus_plus:
372 case lldb::eLanguageTypeObjC:
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");
385 StreamString wrap_stream;
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());
391 // First construct a tagged form of the user expression so we can find it
393 std::string tagged_body;
394 switch (wrapping_language) {
396 tagged_body = m_body;
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);
406 switch (wrapping_language) {
409 case lldb::eLanguageTypeC:
410 wrap_stream.Printf("%s"
412 "%s(void *$__lldb_arg) \n"
417 module_imports.c_str(), m_name.c_str(),
418 lldb_local_var_decls.GetData(), tagged_body.c_str());
420 case lldb::eLanguageTypeC_plus_plus:
421 wrap_stream.Printf("%s"
423 "$__lldb_class::%s(void *$__lldb_arg) \n"
428 module_imports.c_str(), m_name.c_str(),
429 lldb_local_var_decls.GetData(), tagged_body.c_str());
431 case lldb::eLanguageTypeObjC:
435 "@interface $__lldb_objc_class ($__lldb_category) \n"
436 "+(void)%s:(void *)$__lldb_arg; \n"
438 "@implementation $__lldb_objc_class ($__lldb_category) \n"
439 "+(void)%s:(void *)$__lldb_arg \n"
445 module_imports.c_str(), m_name.c_str(), m_name.c_str(),
446 lldb_local_var_decls.GetData(), tagged_body.c_str());
450 "@interface $__lldb_objc_class ($__lldb_category) \n"
451 "-(void)%s:(void *)$__lldb_arg; \n"
453 "@implementation $__lldb_objc_class ($__lldb_category) \n"
454 "-(void)%s:(void *)$__lldb_arg \n"
460 module_imports.c_str(), m_name.c_str(), m_name.c_str(),
461 lldb_local_var_decls.GetData(), tagged_body.c_str());
466 text = wrap_stream.GetString();
474 bool 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;
480 switch (wrapping_language) {
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;
491 start_loc = transformed_text.find(start_marker);
492 if (start_loc == std::string::npos)
494 start_loc += strlen(start_marker);
495 end_loc = transformed_text.find(end_marker);
496 return end_loc != std::string::npos;