Debugger support to print all stored and computed properties

Summary
Currently running po in lldb on a Swift object will print all stored properties. I've come across a few cases where I have a very large model object, with 10s of stored and computed properties. It would be great if there was some option for po or separate command which will print the computed properties also.

Example

Reference the following code for this example. Note that this is a very simple object, and this pitch will mainly benefit large complicated objects.

struct ShoppingBag {
    var apples: Int
    var bananas: Int

    var fruits: Int { return apples + bananas }
}
let bag = ShoppingBag(apples: 3, bananas: 3)

Running (lldb) po bag returns the following:

▿ ShoppingBag
   - apples : 3
   - bananas : 3

I'd like some sort of way to have the following printed:

▿ ShoppingBag
  - apples : 3
  - bananas : 3
  - fruits : 6

Workarounds

There are a few workarounds, the most obvious being just run (lldb) po bag.fruits. Another would be to provide a data formatter for ShoppingBag. One interesting one is to have ShoppingBag conform to CustomReflectable, as follows:

extension ShoppingBag: CustomReflectable {
  var customMirror: Mirror {
    return Mirror(self, children: ["apples": apples, "bananans": bananas, "fruits": fruits], displayStyle: .struct)
  }
}

Then, running (lldb) po bag will return:

(lldb) po bag 
▿ ShoppingBag
  - apples : 3
  - bananans : 3
  - fruits : 6

The main problem with this solution is the implementation of var customMirror: Mirror is pretty messy, even with a simple object like ShoppingBag. The main benefit this pitch would have is for complex objects with many properties, where it would be fairly time consuming & messy to implement var customMirror: Mirror. If the compiler were able to automatically implement this property when CustomReflectable is conformed to, this would solve that issue.

Issues

There are a couple issues with including this behavior by default for po

  1. A computed property can mutate the object (this isn't typical but possible). (lldb) po should generally not mutate any memory.
  2. A computed property may be expensive to compute. Again, this is atypical but still possible.

This could be solved in a few ways, either by adding a flag to lldb to opt in or out of this functionality, or adding a flag to class/struct definitions that tell the expression parser to skip computed properties (or even specific computed properties) when poing the object.

Conclusion

Let me know what your thoughts are/if you have any enhancements to the pitch. Thanks,

Greg

4 Likes

I don't think it is typical for a computed property to mutate the observed state of an object, but I think it is reasonably common for a compute property to compute and store work, and to potentially rely on other properties (even in other objects) that do the same.

From an API contract point of view this isn't exciting (except perhaps that any given access to the property may be a lot slower), but from a debugging point of view it can be a very big deal.

So I think it is important that the existing behavior still be accessible.

1 Like