| //===-- ExpressionSourceCode.cpp --------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "lldb/Expression/ExpressionSourceCode.h" |
| |
| #include "lldb/Core/StreamString.h" |
| #include "Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.h" |
| #include "Plugins/ExpressionParser/Clang/ClangPersistentVariables.h" |
| #include "lldb/Symbol/CompileUnit.h" |
| #include "lldb/Symbol/DebugMacros.h" |
| #include "lldb/Symbol/Block.h" |
| #include "lldb/Symbol/TypeSystem.h" |
| #include "lldb/Symbol/VariableList.h" |
| #include "lldb/Target/ExecutionContext.h" |
| #include "lldb/Target/Language.h" |
| #include "lldb/Target/Platform.h" |
| #include "lldb/Target/StackFrame.h" |
| #include "lldb/Target/Target.h" |
| |
| using namespace lldb_private; |
| |
| const char * |
| ExpressionSourceCode::g_expression_prefix = R"( |
| #ifndef NULL |
| #define NULL (__null) |
| #endif |
| #ifndef Nil |
| #define Nil (__null) |
| #endif |
| #ifndef nil |
| #define nil (__null) |
| #endif |
| #ifndef YES |
| #define YES ((BOOL)1) |
| #endif |
| #ifndef NO |
| #define NO ((BOOL)0) |
| #endif |
| typedef __INT8_TYPE__ int8_t; |
| typedef __UINT8_TYPE__ uint8_t; |
| typedef __INT16_TYPE__ int16_t; |
| typedef __UINT16_TYPE__ uint16_t; |
| typedef __INT32_TYPE__ int32_t; |
| typedef __UINT32_TYPE__ uint32_t; |
| typedef __INT64_TYPE__ int64_t; |
| typedef __UINT64_TYPE__ uint64_t; |
| typedef __INTPTR_TYPE__ intptr_t; |
| typedef __UINTPTR_TYPE__ uintptr_t; |
| typedef __SIZE_TYPE__ size_t; |
| typedef __PTRDIFF_TYPE__ ptrdiff_t; |
| typedef unsigned short unichar; |
| extern "C" |
| { |
| int printf(const char * __restrict, ...); |
| } |
| )"; |
| |
| static const char *c_start_marker = " /*LLDB_BODY_START*/\n "; |
| static const char *c_end_marker = ";\n /*LLDB_BODY_END*/\n"; |
| |
| namespace { |
| |
| class AddMacroState |
| { |
| enum State |
| { |
| CURRENT_FILE_NOT_YET_PUSHED, |
| CURRENT_FILE_PUSHED, |
| CURRENT_FILE_POPPED |
| }; |
| |
| public: |
| AddMacroState(const FileSpec ¤t_file, const uint32_t current_file_line) |
| : m_state(CURRENT_FILE_NOT_YET_PUSHED), |
| m_current_file(current_file), |
| m_current_file_line(current_file_line) |
| { } |
| |
| void |
| StartFile(const FileSpec &file) |
| { |
| m_file_stack.push_back(file); |
| if (file == m_current_file) |
| m_state = CURRENT_FILE_PUSHED; |
| } |
| |
| void |
| EndFile() |
| { |
| if (m_file_stack.size() == 0) |
| return; |
| |
| FileSpec old_top = m_file_stack.back(); |
| m_file_stack.pop_back(); |
| if (old_top == m_current_file) |
| m_state = CURRENT_FILE_POPPED; |
| } |
| |
| // An entry is valid if it occurs before the current line in |
| // the current file. |
| bool |
| IsValidEntry(uint32_t line) |
| { |
| switch (m_state) |
| { |
| case CURRENT_FILE_NOT_YET_PUSHED: |
| return true; |
| case CURRENT_FILE_PUSHED: |
| // If we are in file included in the current file, |
| // the entry should be added. |
| if (m_file_stack.back() != m_current_file) |
| return true; |
| |
| if (line >= m_current_file_line) |
| return false; |
| else |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| private: |
| std::vector<FileSpec> m_file_stack; |
| State m_state; |
| FileSpec m_current_file; |
| uint32_t m_current_file_line; |
| }; |
| |
| } // anonymous namespace |
| |
| static void |
| AddMacros(const DebugMacros *dm, CompileUnit *comp_unit, AddMacroState &state, StreamString &stream) |
| { |
| if (dm == nullptr) |
| return; |
| |
| for (size_t i = 0; i < dm->GetNumMacroEntries(); i++) |
| { |
| const DebugMacroEntry &entry = dm->GetMacroEntryAtIndex(i); |
| uint32_t line; |
| |
| switch (entry.GetType()) |
| { |
| case DebugMacroEntry::DEFINE: |
| if (state.IsValidEntry(entry.GetLineNumber())) |
| stream.Printf("#define %s\n", entry.GetMacroString().AsCString()); |
| else |
| return; |
| break; |
| case DebugMacroEntry::UNDEF: |
| if (state.IsValidEntry(entry.GetLineNumber())) |
| stream.Printf("#undef %s\n", entry.GetMacroString().AsCString()); |
| else |
| return; |
| break; |
| case DebugMacroEntry::START_FILE: |
| line = entry.GetLineNumber(); |
| if (state.IsValidEntry(line)) |
| state.StartFile(entry.GetFileSpec(comp_unit)); |
| else |
| return; |
| break; |
| case DebugMacroEntry::END_FILE: |
| state.EndFile(); |
| break; |
| case DebugMacroEntry::INDIRECT: |
| AddMacros(entry.GetIndirectDebugMacros(), comp_unit, state, stream); |
| break; |
| default: |
| // This is an unknown/invalid entry. Ignore. |
| break; |
| } |
| } |
| } |
| |
| static void |
| AddLocalVariableDecls(const lldb::VariableListSP &var_list_sp, StreamString &stream) |
| { |
| for (size_t i = 0; i < var_list_sp->GetSize(); i++) |
| { |
| lldb::VariableSP var_sp = var_list_sp->GetVariableAtIndex(i); |
| |
| ConstString var_name = var_sp->GetName(); |
| if (!var_name || var_name == ConstString("this") || var_name == ConstString(".block_descriptor")) |
| continue; |
| |
| stream.Printf("using $__lldb_local_vars::%s;\n", var_name.AsCString()); |
| } |
| } |
| |
| bool ExpressionSourceCode::GetText (std::string &text, lldb::LanguageType wrapping_language, bool const_object, bool static_method, ExecutionContext &exe_ctx) const |
| { |
| const char *target_specific_defines = "typedef signed char BOOL;\n"; |
| std::string module_macros; |
| |
| if (Target *target = exe_ctx.GetTargetPtr()) |
| { |
| if (target->GetArchitecture().GetMachine() == llvm::Triple::aarch64) |
| { |
| target_specific_defines = "typedef bool BOOL;\n"; |
| } |
| if (target->GetArchitecture().GetMachine() == llvm::Triple::x86_64) |
| { |
| if (lldb::PlatformSP platform_sp = target->GetPlatform()) |
| { |
| static ConstString g_platform_ios_simulator ("ios-simulator"); |
| if (platform_sp->GetPluginName() == g_platform_ios_simulator) |
| { |
| target_specific_defines = "typedef bool BOOL;\n"; |
| } |
| } |
| } |
| |
| if (ClangModulesDeclVendor *decl_vendor = target->GetClangModulesDeclVendor()) |
| { |
| ClangPersistentVariables *persistent_vars = llvm::cast<ClangPersistentVariables>(target->GetPersistentExpressionStateForLanguage(lldb::eLanguageTypeC)); |
| const ClangModulesDeclVendor::ModuleVector &hand_imported_modules = persistent_vars->GetHandLoadedClangModules(); |
| ClangModulesDeclVendor::ModuleVector modules_for_macros; |
| |
| for (ClangModulesDeclVendor::ModuleID module : hand_imported_modules) |
| { |
| modules_for_macros.push_back(module); |
| } |
| |
| if (target->GetEnableAutoImportClangModules()) |
| { |
| if (StackFrame *frame = exe_ctx.GetFramePtr()) |
| { |
| if (Block *block = frame->GetFrameBlock()) |
| { |
| SymbolContext sc; |
| |
| block->CalculateSymbolContext(&sc); |
| |
| if (sc.comp_unit) |
| { |
| StreamString error_stream; |
| |
| decl_vendor->AddModulesForCompileUnit(*sc.comp_unit, modules_for_macros, error_stream); |
| } |
| } |
| } |
| } |
| |
| decl_vendor->ForEachMacro(modules_for_macros, [&module_macros] (const std::string &expansion) -> bool { |
| module_macros.append(expansion); |
| module_macros.append("\n"); |
| return false; |
| }); |
| } |
| |
| } |
| |
| StreamString debug_macros_stream; |
| StreamString lldb_local_var_decls; |
| if (StackFrame *frame = exe_ctx.GetFramePtr()) |
| { |
| const SymbolContext &sc = frame->GetSymbolContext( |
| lldb:: eSymbolContextCompUnit | lldb::eSymbolContextLineEntry); |
| |
| if (sc.comp_unit && sc.line_entry.IsValid()) |
| { |
| DebugMacros *dm = sc.comp_unit->GetDebugMacros(); |
| if (dm) |
| { |
| AddMacroState state(sc.line_entry.file, sc.line_entry.line); |
| AddMacros(dm, sc.comp_unit, state, debug_macros_stream); |
| } |
| } |
| |
| ConstString object_name; |
| if (Language::LanguageIsCPlusPlus(frame->GetLanguage())) |
| { |
| lldb::VariableListSP var_list_sp = frame->GetInScopeVariableList(false, true); |
| AddLocalVariableDecls(var_list_sp, lldb_local_var_decls); |
| } |
| } |
| |
| if (m_wrap) |
| { |
| switch (wrapping_language) |
| { |
| default: |
| return false; |
| case lldb::eLanguageTypeC: |
| case lldb::eLanguageTypeC_plus_plus: |
| case lldb::eLanguageTypeObjC: |
| break; |
| } |
| |
| StreamString wrap_stream; |
| |
| wrap_stream.Printf("%s\n%s\n%s\n%s\n%s\n", |
| module_macros.c_str(), |
| debug_macros_stream.GetData(), |
| g_expression_prefix, |
| target_specific_defines, |
| m_prefix.c_str()); |
| |
| // First construct a tagged form of the user expression so we can find it later: |
| std::string tagged_body; |
| switch (wrapping_language) |
| { |
| default: |
| tagged_body = m_body; |
| break; |
| case lldb::eLanguageTypeC: |
| case lldb::eLanguageTypeC_plus_plus: |
| case lldb::eLanguageTypeObjC: |
| tagged_body.append(c_start_marker); |
| tagged_body.append(m_body); |
| tagged_body.append(c_end_marker); |
| break; |
| |
| } |
| switch (wrapping_language) |
| { |
| default: |
| break; |
| case lldb::eLanguageTypeC: |
| wrap_stream.Printf("void \n" |
| "%s(void *$__lldb_arg) \n" |
| "{ \n" |
| " %s; \n" |
| "%s" |
| "} \n", |
| m_name.c_str(), |
| lldb_local_var_decls.GetData(), |
| tagged_body.c_str()); |
| break; |
| case lldb::eLanguageTypeC_plus_plus: |
| wrap_stream.Printf("void \n" |
| "$__lldb_class::%s(void *$__lldb_arg) %s\n" |
| "{ \n" |
| " %s; \n" |
| "%s" |
| "} \n", |
| m_name.c_str(), |
| (const_object ? "const" : ""), |
| lldb_local_var_decls.GetData(), |
| tagged_body.c_str()); |
| break; |
| case lldb::eLanguageTypeObjC: |
| if (static_method) |
| { |
| wrap_stream.Printf("@interface $__lldb_objc_class ($__lldb_category) \n" |
| "+(void)%s:(void *)$__lldb_arg; \n" |
| "@end \n" |
| "@implementation $__lldb_objc_class ($__lldb_category) \n" |
| "+(void)%s:(void *)$__lldb_arg \n" |
| "{ \n" |
| "%s" |
| "} \n" |
| "@end \n", |
| m_name.c_str(), |
| m_name.c_str(), |
| tagged_body.c_str()); |
| } |
| else |
| { |
| wrap_stream.Printf("@interface $__lldb_objc_class ($__lldb_category) \n" |
| "-(void)%s:(void *)$__lldb_arg; \n" |
| "@end \n" |
| "@implementation $__lldb_objc_class ($__lldb_category) \n" |
| "-(void)%s:(void *)$__lldb_arg \n" |
| "{ \n" |
| "%s" |
| "} \n" |
| "@end \n", |
| m_name.c_str(), |
| m_name.c_str(), |
| tagged_body.c_str()); |
| } |
| break; |
| } |
| |
| text = wrap_stream.GetString(); |
| } |
| else |
| { |
| text.append(m_body); |
| } |
| |
| return true; |
| } |
| |
| bool |
| ExpressionSourceCode::GetOriginalBodyBounds(std::string transformed_text, |
| lldb::LanguageType wrapping_language, |
| size_t &start_loc, |
| size_t &end_loc) |
| { |
| const char *start_marker; |
| const char *end_marker; |
| |
| switch (wrapping_language) |
| { |
| default: |
| return false; |
| case lldb::eLanguageTypeC: |
| case lldb::eLanguageTypeC_plus_plus: |
| case lldb::eLanguageTypeObjC: |
| start_marker = c_start_marker; |
| end_marker = c_end_marker; |
| break; |
| } |
| |
| start_loc = transformed_text.find(start_marker); |
| if (start_loc == std::string::npos) |
| return false; |
| start_loc += strlen(start_marker); |
| end_loc = transformed_text.find(end_marker); |
| if (end_loc == std::string::npos) |
| return false; |
| return true; |
| } |
| |