diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index 476715ebb9ce1..13daa7eb4e661 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -215,6 +215,8 @@ class TargetProperties : public Properties { bool GetSwiftAutoImportFrameworks() const; + bool GetSwiftUseContextFreePrintObject() const; + bool GetEnableAutoImportClangModules() const; bool GetUseAllCompilerFlags() const; @@ -566,6 +568,14 @@ class EvaluateExpressionOptions { bool GetPreparePlaygroundStubFunctions() const { return m_prepare_playground_stub_functions; } + void SetUseContextFreeSwiftPrintObject(bool enable = true) { + m_use_context_free_swift_print_object = enable; + } + + bool GetUseContextFreeSwiftPrintObject() const { + return m_use_context_free_swift_print_object; + } + private: ExecutionPolicy m_execution_policy = default_execution_policy; SourceLanguage m_language; @@ -607,6 +617,11 @@ class EvaluateExpressionOptions { mutable uint32_t m_pound_line_line = 0; bool m_prepare_playground_stub_functions = true; + /// An evaluation mode that for swift. Has the following effects: + /// 1. Disables implicit module loading + /// 2. Disables compiler availability checking + bool m_use_context_free_swift_print_object = false; + /// During expression evaluation, any SymbolContext in this list will be /// used for symbol/function lookup before any other context (except for /// the module corresponding to the current frame). diff --git a/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp index cd42b0a3da8fd..1c2b8ca5c21d1 100644 --- a/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionParser.cpp @@ -1317,13 +1317,14 @@ SwiftExpressionParser::ParseAndImport( if (lldb::StackFrameSP this_frame_sp = m_stack_frame_wp.lock()) process_sp = this_frame_sp->CalculateProcess(); m_swift_ast_ctx.LoadImplicitModules(m_sc.target_sp, process_sp, *m_exe_scope); - if (!m_swift_ast_ctx.GetImplicitImports(m_sc, process_sp, additional_imports, - implicit_import_error)) { - const char *msg = implicit_import_error.AsCString(); - if (!msg) - msg = "error status positive, but import still failed"; - return make_error(msg); - } + if (!m_options.GetUseContextFreeSwiftPrintObject()) + if (!m_swift_ast_ctx.GetImplicitImports( + m_sc, process_sp, additional_imports, implicit_import_error)) { + const char *msg = implicit_import_error.AsCString(); + if (!msg) + msg = "error status positive, but import still failed"; + return make_error(msg); + } swift::ImplicitImportInfo importInfo; importInfo.StdlibKind = swift::ImplicitStdlibKind::Stdlib; diff --git a/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionSourceCode.cpp b/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionSourceCode.cpp index 80f342d80e9b2..5ed2d89e9a25a 100644 --- a/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionSourceCode.cpp +++ b/lldb/source/Plugins/ExpressionParser/Swift/SwiftExpressionSourceCode.cpp @@ -640,15 +640,22 @@ Status SwiftExpressionSourceCode::GetText( if (triple.isOSDarwin()) { if (auto process_sp = exe_ctx.GetProcessSP()) { os_vers << getAvailabilityName(triple) << " "; - auto platform = target->GetPlatform(); - bool is_simulator = platform->GetPluginName().ends_with("-simulator"); - if (is_simulator) { - // The simulators look like the host OS to Process, but Platform - // can the version out of an environment variable. - os_vers << platform->GetOSVersion(process_sp.get()).getAsString(); + if (options.GetUseContextFreeSwiftPrintObject()) { + // Disable availability by setting the OS version to 9999. This + // placeholder OS version used for future OS versions when building + // the Swift standard library locally. + os_vers << "9999"; } else { - llvm::VersionTuple version = process_sp->GetHostOSVersion(); - os_vers << version.getAsString(); + auto platform = target->GetPlatform(); + bool is_simulator = platform->GetPluginName().ends_with("-simulator"); + if (is_simulator) { + // The simulators look like the host OS to Process, but Platform + // can get the version out of an environment variable. + os_vers << platform->GetOSVersion(process_sp.get()).getAsString(); + } else { + llvm::VersionTuple version = process_sp->GetHostOSVersion(); + os_vers << version.getAsString(); + } } } } diff --git a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp index 9ea5ae4b7411e..cfec40d234093 100644 --- a/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/Swift/SwiftLanguageRuntime.cpp @@ -48,6 +48,7 @@ #include "lldb/Utility/Log.h" #include "lldb/Utility/OptionParsing.h" #include "lldb/Utility/Status.h" +#include "lldb/Utility/StreamString.h" #include "lldb/Utility/StructuredData.h" #include "lldb/Utility/Timer.h" #include "lldb/ValueObject/ValueObject.h" @@ -835,7 +836,7 @@ SwiftLanguageRuntime::GetObjectDescriptionExpr_Ref(ValueObject &object) { object.GetValueAsUnsigned(0)).str(); if (log) - log->Printf("[GetObjectDescriptionExpr_Result] expression: %s", + log->Printf("[GetObjectDescriptionExpr_Ref] expression: %s", expr_string.GetData()); return expr_str; } @@ -911,8 +912,9 @@ std::string SwiftLanguageRuntime::GetObjectDescriptionExpr_Copy( return expr_string; } -llvm::Error SwiftLanguageRuntime::RunObjectDescriptionExpr( - ValueObject &object, std::string &expr_string, Stream &result) { +static llvm::Expected +RunObjectDescription(ValueObject &object, std::string &expr_string, + Process &process, bool context_free = false) { Log *log(GetLog(LLDBLog::DataFormatters | LLDBLog::Expressions)); ValueObjectSP result_sp; EvaluateExpressionOptions eval_options; @@ -920,16 +922,17 @@ llvm::Error SwiftLanguageRuntime::RunObjectDescriptionExpr( eval_options.SetLanguage(lldb::eLanguageTypeSwift); eval_options.SetSuppressPersistentResult(true); eval_options.SetIgnoreBreakpoints(true); - eval_options.SetTimeout(GetProcess().GetUtilityExpressionTimeout()); + eval_options.SetTimeout(process.GetUtilityExpressionTimeout()); + if (context_free) + eval_options.SetUseContextFreeSwiftPrintObject(); StackFrameSP frame_sp = object.GetFrameSP(); if (!frame_sp) - frame_sp = - GetProcess().GetThreadList().GetSelectedThread()->GetSelectedFrame( - DoNoSelectMostRelevantFrame); + frame_sp = process.GetThreadList().GetSelectedThread()->GetSelectedFrame( + DoNoSelectMostRelevantFrame); if (!frame_sp) return llvm::createStringError("no execution context to run expression in"); - auto eval_result = GetProcess().GetTarget().EvaluateExpression( + auto eval_result = process.GetTarget().EvaluateExpression( expr_string, frame_sp.get(), result_sp, eval_options); LLDB_LOG(log, "[RunObjectDescriptionExpr] {0}", toString(eval_result)); @@ -952,12 +955,17 @@ llvm::Error SwiftLanguageRuntime::RunObjectDescriptionExpr( return llvm::createStringError("expression produced invalid result type"); } + return result_sp; +} + +static llvm::Error DumpString(ValueObjectSP result_sp, Stream &strm) { + Log *log(GetLog(LLDBLog::DataFormatters | LLDBLog::Expressions)); formatters::StringPrinter::ReadStringAndDumpToStreamOptions dump_options; dump_options.SetEscapeNonPrintables(false); dump_options.SetQuote('\0'); dump_options.SetPrefixToken(nullptr); if (formatters::swift::String_SummaryProvider( - *result_sp.get(), result, + *result_sp, strm, TypeSummaryOptions() .SetLanguage(lldb::eLanguageTypeSwift) .SetCapping(eTypeSummaryUncapped), @@ -973,6 +981,15 @@ llvm::Error SwiftLanguageRuntime::RunObjectDescriptionExpr( return llvm::createStringError("expression produced unprintable string"); } +llvm::Error SwiftLanguageRuntime::RunObjectDescriptionExpr( + ValueObject &object, std::string &expr_string, Stream &strm) { + auto result_or_err = RunObjectDescription(object, expr_string, GetProcess()); + if (!result_or_err) + return result_or_err.takeError(); + + return DumpString(*result_or_err, strm); +} + static bool IsVariable(ValueObject &object) { if (object.IsSynthetic()) return IsVariable(*object.GetNonSyntheticValue()); @@ -1002,11 +1019,80 @@ static bool IsSwiftReferenceType(ValueObject &object) { return false; } +static llvm::Error PrintObjectViaPointer(Stream &strm, ValueObject &object, + Process &process) { + Flags flags(object.GetCompilerType().GetTypeInfo()); + addr_t addr = LLDB_INVALID_ADDRESS; + if (flags.Test(eTypeInstanceIsPointer)) { + // Objects are pointers. + addr = object.GetValueAsUnsigned(LLDB_INVALID_ADDRESS); + addr = process.FixDataAddress(addr); + } else { + // Get the address of non-object values (structs, enums). + auto addr_and_type = object.GetAddressOf(false); + if (addr_and_type.type != eAddressTypeLoad) { + return llvm::createStringError("address-of value object failed"); + } + addr = addr_and_type.address; + } + + if (addr == 0 || addr == LLDB_INVALID_ADDRESS) + return llvm::createStringError("invalid address 0x%x", addr); + + StringRef mangled_type_name = object.GetMangledTypeName(); + // Swift APIs that receive mangled names require the prefix removed. + mangled_type_name.consume_front("$s"); + mangled_type_name.consume_front("$e"); // Embedded Swift prefix + + std::string expr_string = + llvm::formatv( + "Swift._DebuggerSupport.stringForPrintObject(UnsafeRawPointer(" + "bitPattern: {0}), mangledTypeName: \"{1}\")", + addr, mangled_type_name) + .str(); + + auto result_or_err = RunObjectDescription(object, expr_string, process, true); + if (!result_or_err) + return result_or_err.takeError(); + + // A `(Bool, String)` tuple, where the bool indicates success/failure. + auto result_sp = *result_or_err; + auto success_sp = result_sp->GetChildAtIndex(0); + auto description_sp = result_sp->GetChildAtIndex(1); + + StreamString string_result; + auto err = DumpString(description_sp, string_result); + if (err) { + return llvm::joinErrors( + std::move(err), + llvm::createStringError("decoding of description String failed")); + } + + Status status; + if (!success_sp->IsLogicalTrue(status)) + // The Bool is false, which means the String is an error message. + return llvm::createStringError(string_result.GetData()); + + strm.PutCString(string_result.GetString()); + return llvm::Error::success(); +} + llvm::Error SwiftLanguageRuntime::GetObjectDescription(Stream &str, ValueObject &object) { if (object.IsUninitializedReference()) return llvm::createStringError(""); + Log *log = GetLog(LLDBLog::DataFormatters | LLDBLog::Expressions); + if (GetProcess().GetTarget().GetSwiftUseContextFreePrintObject()) { + if (auto err = PrintObjectViaPointer(str, object, GetProcess())) { + LLDB_LOG_ERROR(log, std::move(err), + "stringForPrintObject(_:mangledTypeName) failed: {0}"); + } else { + LLDB_LOG(log, "stringForPrintObject(_:mangledTypeName:) succeeded"); + return llvm::Error::success(); + } + } + std::string expr_string; if (::IsVariable(object) || ::IsSwiftResultVariable(object.GetName())) { diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index cd977fcaeeedd..7bc75d664e8fc 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -4705,6 +4705,18 @@ bool TargetProperties::GetSwiftAutoImportFrameworks() const { idx, g_target_properties[idx].default_uint_value != 0); } +bool TargetProperties::GetSwiftUseContextFreePrintObject() const { + const Property *exp_property = + m_collection_sp->GetPropertyAtIndex(ePropertyExperimental); + OptionValueProperties *exp_values = + exp_property->GetValue()->GetAsProperties(); + if (exp_values) + return exp_values + ->GetPropertyAtIndexAs(ePropertySwiftUseContextFreePrintObject) + .value_or(false); + return false; +} + void TargetProperties::SetUseDIL(ExecutionContext *exe_ctx, bool b) { const Property *exp_property = m_collection_sp->GetPropertyAtIndex(ePropertyExperimental, exe_ctx); diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td index 62c8290847576..7d7263ece5c48 100644 --- a/lldb/source/Target/TargetProperties.td +++ b/lldb/source/Target/TargetProperties.td @@ -37,6 +37,9 @@ let Definition = "target_experimental" in { def SwiftCacheTaskPointerLocation: Property<"swift-cache-task-pointer-location", "Boolean">, DefaultTrue, Desc<"Enables caching of task pointers inside the swift tasks plugin">; + def SwiftUseContextFreePrintObject: Property<"swift-use-context-free-po", "Boolean">, + DefaultFalse, + Desc<"If true, use the context-free po implementation for Swift.">; def UseDIL : Property<"use-DIL", "Boolean">, Global, DefaultTrue, Desc<"If true, use the DIL implementation for frame variable evaluation.">; diff --git a/lldb/test/API/lang/swift/expression/error_reporting/TestSwiftExpressionErrorReporting.py b/lldb/test/API/lang/swift/expression/error_reporting/TestSwiftExpressionErrorReporting.py index ca543525dc038..e665b77e9635e 100644 --- a/lldb/test/API/lang/swift/expression/error_reporting/TestSwiftExpressionErrorReporting.py +++ b/lldb/test/API/lang/swift/expression/error_reporting/TestSwiftExpressionErrorReporting.py @@ -68,6 +68,8 @@ def test_missing_type(self): target, process, thread, bkpt = lldbutil.run_to_source_breakpoint( self, 'break here', lldb.SBFileSpec('main.swift')) + self.runCmd("settings set target.experimental.swift-use-context-free-po true") + options = lldb.SBExpressionOptions() value = self.frame().EvaluateExpression("strct", options) def check(value): @@ -81,12 +83,14 @@ def check(value): check(value) + # This succeeds using stringForPrintObject(_:mangledTypeName:), which + # doesn't require the type to be available. + # Note: (?s)^(?!.*) checks that the pattern is not found. self.expect( "dwim-print -O -- strct", - substrs=["error: Missing type", "properties = true"], + patterns=["(?s)^(?!.*error: Missing type)", "properties : true"], ) process.Continue() self.expect('expression -O -- number', error=True, substrs=['self', 'not', 'found']) - diff --git a/lldb/test/API/lang/swift/po/pointer_and_mangled_typename/Makefile b/lldb/test/API/lang/swift/po/pointer_and_mangled_typename/Makefile new file mode 100644 index 0000000000000..cca30b939e652 --- /dev/null +++ b/lldb/test/API/lang/swift/po/pointer_and_mangled_typename/Makefile @@ -0,0 +1,3 @@ +SWIFT_SOURCES := main.swift +SWIFTFLAGS_EXTRAS := -parse-as-library +include Makefile.rules diff --git a/lldb/test/API/lang/swift/po/pointer_and_mangled_typename/TestSwiftPrintObjectPointerAndTypeName.py b/lldb/test/API/lang/swift/po/pointer_and_mangled_typename/TestSwiftPrintObjectPointerAndTypeName.py new file mode 100644 index 0000000000000..a3129e9779a53 --- /dev/null +++ b/lldb/test/API/lang/swift/po/pointer_and_mangled_typename/TestSwiftPrintObjectPointerAndTypeName.py @@ -0,0 +1,127 @@ +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * + + +class TestCase(TestBase): + + def setUp(self): + TestBase.setUp(self) + self.runCmd("settings set target.experimental.swift-use-context-free-po true") + + self.log = self.getBuildArtifact("expr.log") + self.runCmd(f"log enable lldb expr -f {self.log}") + + def _filecheck(self, key): + self.filecheck(f"platform shell cat {self.log}", __file__, f"-check-prefix=CHECK-{key}") + + @swiftTest + def test_int(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "break int", lldb.SBFileSpec("main.swift") + ) + self.expect("po value", substrs=["2025"]) + self._filecheck("INT") + # CHECK-INT: stringForPrintObject(UnsafeRawPointer(bitPattern: {{[0-9]+}}), mangledTypeName: "SiD") + + @swiftTest + def test_string(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "break string", lldb.SBFileSpec("main.swift") + ) + self.expect("po value", substrs=["Po"]) + self._filecheck("STRING") + # CHECK-STRING: stringForPrintObject(UnsafeRawPointer(bitPattern: {{[0-9]+}}), mangledTypeName: "SSD") + + @swiftTest + def test_struct(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "break struct", lldb.SBFileSpec("main.swift") + ) + self.expect("po value", substrs=["▿ Struct"]) + self._filecheck("STRUCT") + # CHECK-STRUCT: stringForPrintObject(UnsafeRawPointer(bitPattern: {{[0-9]+}}), mangledTypeName: "1a6StructVD") + + @swiftTest + def test_class(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "break class", lldb.SBFileSpec("main.swift") + ) + self.expect("po value", substrs=[""]) + self._filecheck("GEN-STRUCT") + # CHECK-GEN-STRUCT: stringForPrintObject(UnsafeRawPointer(bitPattern: {{[0-9]+}}), mangledTypeName: "1a13GenericStructVySSGD") + + @swiftTest + def test_generic_class(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "break generic class", lldb.SBFileSpec("main.swift") + ) + self.expect("po value", substrs=[": 0x"]) + self._filecheck("GEN-CLASS") + # CHECK-GEN-CLASS: stringForPrintObject(UnsafeRawPointer(bitPattern: {{[0-9]+}}), mangledTypeName: "1a12GenericClassCySSGD") + + @swiftTest + def test_generic_enum(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "break generic enum", lldb.SBFileSpec("main.swift") + ) + self.expect("po value", substrs=["▿ GenericEnum"]) + self._filecheck("GEN-ENUM") + # CHECK-GEN-ENUM: stringForPrintObject(UnsafeRawPointer(bitPattern: {{.*}}), mangledTypeName: "1a11GenericEnumOySSGD") + + @swiftTest + def test_described_struct(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "break described struct", lldb.SBFileSpec("main.swift") + ) + self.expect("po value", substrs=["DescribedStruct"]) + self._filecheck("DESC-STRUCT") + # CHECK-DESC-STRUCT: stringForPrintObject(UnsafeRawPointer(bitPattern: {{[0-9]+}}), mangledTypeName: "1a15DescribedStructVD") + + @swiftTest + def test_described_class(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "break described class", lldb.SBFileSpec("main.swift") + ) + self.expect("po value", substrs=["DescribedClass"]) + self._filecheck("DESC-CLASS") + # CHECK-DESC-CLASS: stringForPrintObject(UnsafeRawPointer(bitPattern: {{[0-9]+}}), mangledTypeName: "1a14DescribedClassCD") + + @swiftTest + def test_described_enum(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "break described enum", lldb.SBFileSpec("main.swift") + ) + self.expect("po value", substrs=["DescribedEnum"]) + self._filecheck("DESC-ENUM") + # CHECK-DESC-ENUM: stringForPrintObject(UnsafeRawPointer(bitPattern: {{.*}}), mangledTypeName: "1a13DescribedEnumOD") diff --git a/lldb/test/API/lang/swift/po/pointer_and_mangled_typename/main.swift b/lldb/test/API/lang/swift/po/pointer_and_mangled_typename/main.swift new file mode 100644 index 0000000000000..2a5c0fc198b45 --- /dev/null +++ b/lldb/test/API/lang/swift/po/pointer_and_mangled_typename/main.swift @@ -0,0 +1,97 @@ +struct Struct { + var s = "Po" + var n = 2025 +} + +class Class { + var s = "Po" + var n = 2025 +} + +enum Enum { + case zero + case pair(String, Int) +} + +struct GenericStruct { + var s: T + var n = 2025 +} + +class GenericClass { + var s: T + var n = 2025 + init(s: T) { self.s = s } +} + +enum GenericEnum { + case zero + case pair(T, Int) +} + +struct DescribedStruct: CustomStringConvertible { + var s = "Po" + var n = 2025 + var description: String { "DescribedStruct" } +} + +class DescribedClass: CustomStringConvertible { + var s = "Po" + var n = 2025 + var description: String { "DescribedClass" } +} + +enum DescribedEnum: CustomStringConvertible { + case zero + case pair(String, Int) + var description: String { "DescribedEnum" } +} + +@main struct Entry { + static func main() { + do { + var value = 2025 + print("break int") + } + do { + var value = "Po" + print("break string") + } + do { + let value = Struct() + print("break struct") + } + do { + let value = Class() + print("break class") + } + do { + let value = Enum.pair("Po", 50) + print("break enum") + } + do { + let value = GenericStruct(s: "Po") + print("break generic struct") + } + do { + let value = GenericClass(s: "Po") + print("break generic class") + } + do { + let value = GenericEnum.pair("Po", 50) + print("break generic enum") + } + do { + let value = DescribedStruct() + print("break described struct") + } + do { + let value = DescribedClass() + print("break described class") + } + do { + let value = DescribedEnum.pair("Po", 50) + print("break described enum") + } + } +}