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 ¤t_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