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 "llvm/ADT/StringRef.h" 13 14 #include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h" 15 #include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h" 16 #include "lldb/Symbol/Block.h" 17 #include "lldb/Symbol/CompileUnit.h" 18 #include "lldb/Symbol/DebugMacros.h" 19 #include "lldb/Symbol/TypeSystem.h" 20 #include "lldb/Symbol/VariableList.h" 21 #include "lldb/Target/ExecutionContext.h" 22 #include "lldb/Target/Language.h" 23 #include "lldb/Target/Platform.h" 24 #include "lldb/Target/StackFrame.h" 25 #include "lldb/Target/Target.h" 26 #include "lldb/Utility/StreamString.h" 27 28 using namespace lldb_private; 29 30 const char *ClangExpressionSourceCode::g_expression_prefix = R"( 31 #ifndef NULL 32 #define NULL (__null) 33 #endif 34 #ifndef Nil 35 #define Nil (__null) 36 #endif 37 #ifndef nil 38 #define nil (__null) 39 #endif 40 #ifndef YES 41 #define YES ((BOOL)1) 42 #endif 43 #ifndef NO 44 #define NO ((BOOL)0) 45 #endif 46 typedef __INT8_TYPE__ int8_t; 47 typedef __UINT8_TYPE__ uint8_t; 48 typedef __INT16_TYPE__ int16_t; 49 typedef __UINT16_TYPE__ uint16_t; 50 typedef __INT32_TYPE__ int32_t; 51 typedef __UINT32_TYPE__ uint32_t; 52 typedef __INT64_TYPE__ int64_t; 53 typedef __UINT64_TYPE__ uint64_t; 54 typedef __INTPTR_TYPE__ intptr_t; 55 typedef __UINTPTR_TYPE__ uintptr_t; 56 typedef __SIZE_TYPE__ size_t; 57 typedef __PTRDIFF_TYPE__ ptrdiff_t; 58 typedef unsigned short unichar; 59 extern "C" 60 { 61 int printf(const char * __restrict, ...); 62 } 63 )"; 64 65 static const char *c_start_marker = " /*LLDB_BODY_START*/\n "; 66 static const char *c_end_marker = ";\n /*LLDB_BODY_END*/\n"; 67 68 namespace { 69 70 class AddMacroState { 71 enum State { 72 CURRENT_FILE_NOT_YET_PUSHED, 73 CURRENT_FILE_PUSHED, 74 CURRENT_FILE_POPPED 75 }; 76 77 public: 78 AddMacroState(const FileSpec ¤t_file, const uint32_t current_file_line) 79 : m_state(CURRENT_FILE_NOT_YET_PUSHED), m_current_file(current_file), 80 m_current_file_line(current_file_line) {} 81 82 void StartFile(const FileSpec &file) { 83 m_file_stack.push_back(file); 84 if (file == m_current_file) 85 m_state = CURRENT_FILE_PUSHED; 86 } 87 88 void EndFile() { 89 if (m_file_stack.size() == 0) 90 return; 91 92 FileSpec old_top = m_file_stack.back(); 93 m_file_stack.pop_back(); 94 if (old_top == m_current_file) 95 m_state = CURRENT_FILE_POPPED; 96 } 97 98 // An entry is valid if it occurs before the current line in the current 99 // file. 100 bool IsValidEntry(uint32_t line) { 101 switch (m_state) { 102 case CURRENT_FILE_NOT_YET_PUSHED: 103 return true; 104 case CURRENT_FILE_PUSHED: 105 // If we are in file included in the current file, the entry should be 106 // added. 107 if (m_file_stack.back() != m_current_file) 108 return true; 109 110 return line < m_current_file_line; 111 default: 112 return false; 113 } 114 } 115 116 private: 117 std::vector<FileSpec> m_file_stack; 118 State m_state; 119 FileSpec m_current_file; 120 uint32_t m_current_file_line; 121 }; 122 123 } // anonymous namespace 124 125 static void AddMacros(const DebugMacros *dm, CompileUnit *comp_unit, 126 AddMacroState &state, StreamString &stream) { 127 if (dm == nullptr) 128 return; 129 130 for (size_t i = 0; i < dm->GetNumMacroEntries(); i++) { 131 const DebugMacroEntry &entry = dm->GetMacroEntryAtIndex(i); 132 uint32_t line; 133 134 switch (entry.GetType()) { 135 case DebugMacroEntry::DEFINE: 136 if (state.IsValidEntry(entry.GetLineNumber())) 137 stream.Printf("#define %s\n", entry.GetMacroString().AsCString()); 138 else 139 return; 140 break; 141 case DebugMacroEntry::UNDEF: 142 if (state.IsValidEntry(entry.GetLineNumber())) 143 stream.Printf("#undef %s\n", entry.GetMacroString().AsCString()); 144 else 145 return; 146 break; 147 case DebugMacroEntry::START_FILE: 148 line = entry.GetLineNumber(); 149 if (state.IsValidEntry(line)) 150 state.StartFile(entry.GetFileSpec(comp_unit)); 151 else 152 return; 153 break; 154 case DebugMacroEntry::END_FILE: 155 state.EndFile(); 156 break; 157 case DebugMacroEntry::INDIRECT: 158 AddMacros(entry.GetIndirectDebugMacros(), comp_unit, state, stream); 159 break; 160 default: 161 // This is an unknown/invalid entry. Ignore. 162 break; 163 } 164 } 165 } 166 167 /// Checks if the expression body contains the given variable as a token. 168 /// \param body The expression body. 169 /// \param var The variable token we are looking for. 170 /// \return True iff the expression body containes the variable as a token. 171 static bool ExprBodyContainsVar(llvm::StringRef body, llvm::StringRef var) { 172 assert(var.find_if([](char c) { return !clang::isIdentifierBody(c); }) == 173 llvm::StringRef::npos && 174 "variable contains non-identifier chars?"); 175 176 size_t start = 0; 177 // Iterate over all occurences of the variable string in our expression. 178 while ((start = body.find(var, start)) != llvm::StringRef::npos) { 179 // We found our variable name in the expression. Check that the token 180 // that contains our needle is equal to our variable and not just contains 181 // the character sequence by accident. 182 // Prevents situations where we for example inlcude the variable 'FOO' in an 183 // expression like 'FOObar + 1'. 184 bool has_characters_before = 185 start != 0 && clang::isIdentifierBody(body[start - 1]); 186 bool has_characters_after = 187 start + var.size() < body.size() && 188 clang::isIdentifierBody(body[start + var.size()]); 189 190 // Our token just contained the variable name as a substring. Continue 191 // searching the rest of the expression. 192 if (has_characters_before || has_characters_after) { 193 ++start; 194 continue; 195 } 196 return true; 197 } 198 return false; 199 } 200 201 static void AddLocalVariableDecls(const lldb::VariableListSP &var_list_sp, 202 StreamString &stream, 203 const std::string &expr, 204 lldb::LanguageType wrapping_language) { 205 for (size_t i = 0; i < var_list_sp->GetSize(); i++) { 206 lldb::VariableSP var_sp = var_list_sp->GetVariableAtIndex(i); 207 208 ConstString var_name = var_sp->GetName(); 209 210 211 // We can check for .block_descriptor w/o checking for langauge since this 212 // is not a valid identifier in either C or C++. 213 if (!var_name || var_name == ConstString(".block_descriptor")) 214 continue; 215 216 if (!expr.empty() && !ExprBodyContainsVar(expr, var_name.GetStringRef())) 217 continue; 218 219 if ((var_name == ConstString("self") || var_name == ConstString("_cmd")) && 220 (wrapping_language == lldb::eLanguageTypeObjC || 221 wrapping_language == lldb::eLanguageTypeObjC_plus_plus)) 222 continue; 223 224 if (var_name == ConstString("this") && 225 wrapping_language == lldb::eLanguageTypeC_plus_plus) 226 continue; 227 228 stream.Printf("using $__lldb_local_vars::%s;\n", var_name.AsCString()); 229 } 230 } 231 232 bool ClangExpressionSourceCode::GetText( 233 std::string &text, lldb::LanguageType wrapping_language, bool static_method, 234 ExecutionContext &exe_ctx, bool add_locals, bool force_add_all_locals, 235 llvm::ArrayRef<std::string> modules) const { 236 const char *target_specific_defines = "typedef signed char BOOL;\n"; 237 std::string module_macros; 238 239 Target *target = exe_ctx.GetTargetPtr(); 240 if (target) { 241 if (target->GetArchitecture().GetMachine() == llvm::Triple::aarch64) { 242 target_specific_defines = "typedef bool BOOL;\n"; 243 } 244 if (target->GetArchitecture().GetMachine() == llvm::Triple::x86_64) { 245 if (lldb::PlatformSP platform_sp = target->GetPlatform()) { 246 static ConstString g_platform_ios_simulator("ios-simulator"); 247 if (platform_sp->GetPluginName() == g_platform_ios_simulator) { 248 target_specific_defines = "typedef bool BOOL;\n"; 249 } 250 } 251 } 252 253 if (ClangModulesDeclVendor *decl_vendor = 254 target->GetClangModulesDeclVendor()) { 255 ClangPersistentVariables *persistent_vars = 256 llvm::cast<ClangPersistentVariables>( 257 target->GetPersistentExpressionStateForLanguage( 258 lldb::eLanguageTypeC)); 259 const ClangModulesDeclVendor::ModuleVector &hand_imported_modules = 260 persistent_vars->GetHandLoadedClangModules(); 261 ClangModulesDeclVendor::ModuleVector modules_for_macros; 262 263 for (ClangModulesDeclVendor::ModuleID module : hand_imported_modules) { 264 modules_for_macros.push_back(module); 265 } 266 267 if (target->GetEnableAutoImportClangModules()) { 268 if (StackFrame *frame = exe_ctx.GetFramePtr()) { 269 if (Block *block = frame->GetFrameBlock()) { 270 SymbolContext sc; 271 272 block->CalculateSymbolContext(&sc); 273 274 if (sc.comp_unit) { 275 StreamString error_stream; 276 277 decl_vendor->AddModulesForCompileUnit( 278 *sc.comp_unit, modules_for_macros, error_stream); 279 } 280 } 281 } 282 } 283 284 decl_vendor->ForEachMacro( 285 modules_for_macros, 286 [&module_macros](const std::string &expansion) -> bool { 287 module_macros.append(expansion); 288 module_macros.append("\n"); 289 return false; 290 }); 291 } 292 } 293 294 StreamString debug_macros_stream; 295 StreamString lldb_local_var_decls; 296 if (StackFrame *frame = exe_ctx.GetFramePtr()) { 297 const SymbolContext &sc = frame->GetSymbolContext( 298 lldb::eSymbolContextCompUnit | lldb::eSymbolContextLineEntry); 299 300 if (sc.comp_unit && sc.line_entry.IsValid()) { 301 DebugMacros *dm = sc.comp_unit->GetDebugMacros(); 302 if (dm) { 303 AddMacroState state(sc.line_entry.file, sc.line_entry.line); 304 AddMacros(dm, sc.comp_unit, state, debug_macros_stream); 305 } 306 } 307 308 if (add_locals) 309 if (target->GetInjectLocalVariables(&exe_ctx)) { 310 lldb::VariableListSP var_list_sp = 311 frame->GetInScopeVariableList(false, true); 312 AddLocalVariableDecls(var_list_sp, lldb_local_var_decls, 313 force_add_all_locals ? "" : m_body, 314 wrapping_language); 315 } 316 } 317 318 if (m_wrap) { 319 switch (wrapping_language) { 320 default: 321 return false; 322 case lldb::eLanguageTypeC: 323 case lldb::eLanguageTypeC_plus_plus: 324 case lldb::eLanguageTypeObjC: 325 break; 326 } 327 328 // Generate a list of @import statements that will import the specified 329 // module into our expression. 330 std::string module_imports; 331 for (const std::string &module : modules) { 332 module_imports.append("@import "); 333 module_imports.append(module); 334 module_imports.append(";\n"); 335 } 336 337 StreamString wrap_stream; 338 339 wrap_stream.Printf("%s\n%s\n%s\n%s\n%s\n", module_macros.c_str(), 340 debug_macros_stream.GetData(), g_expression_prefix, 341 target_specific_defines, m_prefix.c_str()); 342 343 // First construct a tagged form of the user expression so we can find it 344 // later: 345 std::string tagged_body; 346 switch (wrapping_language) { 347 default: 348 tagged_body = m_body; 349 break; 350 case lldb::eLanguageTypeC: 351 case lldb::eLanguageTypeC_plus_plus: 352 case lldb::eLanguageTypeObjC: 353 tagged_body.append(c_start_marker); 354 tagged_body.append(m_body); 355 tagged_body.append(c_end_marker); 356 break; 357 } 358 switch (wrapping_language) { 359 default: 360 break; 361 case lldb::eLanguageTypeC: 362 wrap_stream.Printf("%s" 363 "void \n" 364 "%s(void *$__lldb_arg) \n" 365 "{ \n" 366 " %s; \n" 367 "%s" 368 "} \n", 369 module_imports.c_str(), m_name.c_str(), 370 lldb_local_var_decls.GetData(), tagged_body.c_str()); 371 break; 372 case lldb::eLanguageTypeC_plus_plus: 373 wrap_stream.Printf("%s" 374 "void \n" 375 "$__lldb_class::%s(void *$__lldb_arg) \n" 376 "{ \n" 377 " %s; \n" 378 "%s" 379 "} \n", 380 module_imports.c_str(), m_name.c_str(), 381 lldb_local_var_decls.GetData(), tagged_body.c_str()); 382 break; 383 case lldb::eLanguageTypeObjC: 384 if (static_method) { 385 wrap_stream.Printf( 386 "%s" 387 "@interface $__lldb_objc_class ($__lldb_category) \n" 388 "+(void)%s:(void *)$__lldb_arg; \n" 389 "@end \n" 390 "@implementation $__lldb_objc_class ($__lldb_category) \n" 391 "+(void)%s:(void *)$__lldb_arg \n" 392 "{ \n" 393 " %s; \n" 394 "%s" 395 "} \n" 396 "@end \n", 397 module_imports.c_str(), m_name.c_str(), m_name.c_str(), 398 lldb_local_var_decls.GetData(), tagged_body.c_str()); 399 } else { 400 wrap_stream.Printf( 401 "%s" 402 "@interface $__lldb_objc_class ($__lldb_category) \n" 403 "-(void)%s:(void *)$__lldb_arg; \n" 404 "@end \n" 405 "@implementation $__lldb_objc_class ($__lldb_category) \n" 406 "-(void)%s:(void *)$__lldb_arg \n" 407 "{ \n" 408 " %s; \n" 409 "%s" 410 "} \n" 411 "@end \n", 412 module_imports.c_str(), m_name.c_str(), m_name.c_str(), 413 lldb_local_var_decls.GetData(), tagged_body.c_str()); 414 } 415 break; 416 } 417 418 text = wrap_stream.GetString(); 419 } else { 420 text.append(m_body); 421 } 422 423 return true; 424 } 425 426 bool ClangExpressionSourceCode::GetOriginalBodyBounds( 427 std::string transformed_text, lldb::LanguageType wrapping_language, 428 size_t &start_loc, size_t &end_loc) { 429 const char *start_marker; 430 const char *end_marker; 431 432 switch (wrapping_language) { 433 default: 434 return false; 435 case lldb::eLanguageTypeC: 436 case lldb::eLanguageTypeC_plus_plus: 437 case lldb::eLanguageTypeObjC: 438 start_marker = c_start_marker; 439 end_marker = c_end_marker; 440 break; 441 } 442 443 start_loc = transformed_text.find(start_marker); 444 if (start_loc == std::string::npos) 445 return false; 446 start_loc += strlen(start_marker); 447 end_loc = transformed_text.find(end_marker); 448 return end_loc != std::string::npos; 449 } 450