[lldb] Suppress persistent result when running po
authorDave Lee <davelee.com@gmail.com>
Tue, 14 Feb 2023 21:48:07 +0000 (13:48 -0800)
committerDave Lee <davelee.com@gmail.com>
Fri, 17 Feb 2023 05:05:19 +0000 (21:05 -0800)
Remove the persistent result variable after executing `po`.

Without this change, the following behavior happens:

```
(lldb) p thing
(NSObject *) $0 = 0x600000008000
(lldb) po thing
<NSObject: 0x600000008000>
(lldb) p thing
(NSObject *) $2 = 0x600000008000
(lldb) p $1
(NSObject *) $1 = 0x600000008000
```

Even though `po` hides the persistent result variable, it's still created - as $1 in
this example. It can be accessed even though its existence is not evident.

With this change, the persistent result is removed after the object description has
printed. Instead, this is the behavior:

```
(lldb) p thing
(NSObject *) $0 = 0x600000008000
(lldb) po thing
<NSObject: 0x600000008000>
(lldb) p thing
(NSObject *) $1 = 0x600000008000
```

The difference here is that the `po` doens't silently create a persistent result.

Differential Revision: https://reviews.llvm.org/D144044

lldb/source/Commands/CommandObjectExpression.cpp
lldb/test/API/commands/expression/po_persistent_result/Makefile [new file with mode: 0644]
lldb/test/API/commands/expression/po_persistent_result/TestPoPersistentResult.py [new file with mode: 0644]
lldb/test/API/commands/expression/po_persistent_result/main.m [new file with mode: 0644]

index 9cce009..9402000 100644 (file)
@@ -21,6 +21,7 @@
 #include "lldb/Target/Process.h"
 #include "lldb/Target/StackFrame.h"
 #include "lldb/Target/Target.h"
+#include "lldb/lldb-private-enumerations.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -346,6 +347,9 @@ EvaluateExpressionOptions
 CommandObjectExpression::GetEvalOptions(const Target &target) {
   EvaluateExpressionOptions options;
   options.SetCoerceToId(m_varobj_options.use_objc);
+  if (m_command_options.m_verbosity ==
+      eLanguageRuntimeDescriptionDisplayVerbosityCompact)
+    options.SetSuppressPersistentResult(m_varobj_options.use_objc);
   options.SetUnwindOnError(m_command_options.unwind_on_error);
   options.SetIgnoreBreakpoints(m_command_options.ignore_breakpoints);
   options.SetKeepInMemory(true);
diff --git a/lldb/test/API/commands/expression/po_persistent_result/Makefile b/lldb/test/API/commands/expression/po_persistent_result/Makefile
new file mode 100644 (file)
index 0000000..845553d
--- /dev/null
@@ -0,0 +1,3 @@
+OBJC_SOURCES := main.m
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/expression/po_persistent_result/TestPoPersistentResult.py b/lldb/test/API/commands/expression/po_persistent_result/TestPoPersistentResult.py
new file mode 100644 (file)
index 0000000..3c3b863
--- /dev/null
@@ -0,0 +1,31 @@
+"""
+Test behavior of `po` and persistent results.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestCase(TestBase):
+    def setUp(self):
+        TestBase.setUp(self)
+        self.build()
+        lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.m"))
+
+    def test_po_does_not_print_persistent_result(self):
+        """Test `po` doesn't advertise a persistent result variable."""
+        self.expect("po obj", matching=False, substrs=["$0 = "])
+
+    def test_po_does_not_keep_persistent_result(self):
+        """Test `po` doesn't leak a persistent result variable."""
+        self.expect("po obj")
+        # Verify `po` used a temporary persistent result. In other words, there
+        # should be no $0 at this point.
+        self.expect("expression $0", error=True)
+        self.expect("expression obj", substrs=["$0 = "])
+
+    def test_expression_description_verbosity(self):
+        """Test printing object description _and_ opt-in to persistent results."""
+        self.expect("expression -O -vfull -- obj", substrs=["$0 = "])
+        self.expect("expression $0", substrs=["$0 = "])
diff --git a/lldb/test/API/commands/expression/po_persistent_result/main.m b/lldb/test/API/commands/expression/po_persistent_result/main.m
new file mode 100644 (file)
index 0000000..dfd557e
--- /dev/null
@@ -0,0 +1,6 @@
+#import <objc/NSObject.h>
+
+int main() {
+  NSObject *obj = [NSObject new];
+  return 0; // break here
+}