Skip to content

[4.2] Bring the new PlaygroundLogger implementation into swift-4.2-branch #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 81 commits into from
Jul 19, 2018

Conversation

cwakamo
Copy link
Member

@cwakamo cwakamo commented Jul 10, 2018

This pull request brings the new PlaygroundLogger implementation into swift-4.2-branch by merging master into swift-4.2-branch.

This is required to ensure that the PlaygroundLogger in Swift 4.2 correctly honors CustomPlaygroundDisplayConvertible when logging objects.

This is for rdar://problem/41868882, and depends on swiftlang/swift#17861.

Unlike the playground transform runtime supplied by lldb, this framework provides concrete function values which can be set during initialization.
The intention is that separate frameworks like PlaygroundLogger will interact with the PlaygroundRuntime framework to register their entry points with the runtime.
Then, playground compilation simply needs to import this framework, and the playground transform will automatically insert calls to these function values.
Although this framework is named with "playground" in the name, it also provides runtime entry points for the PC macro.
…rectory, in preparation for a new PlaygroundLogger framework.
This will be a brand new implementation of PlaygroundLogger, supporting the same stuff as the old library but hopefully in a more maintainable, testable manner.
This version of PlaygroundLogger is intended to work with the new PlaygroundRuntime framework, but entry points for use with legacy lldb will be provided as well. (Those are not yet present.)
This is `initializePlaygroundLogger()`, which sets up the `_playgroundPrintHook` function with the standard library and the various `$builtin` functions with PlaygroundRuntime.
This commit includes creating a `printHook` function, but as with the other logger entrypoints, it currently just calls `fatalError`.

As part of this, began passing `-Xfrontend -debugger-support` when building PlaygroundLogger so it can use dollar identifiers.
A `LogEntry` represents an item which has been logged, whereas a `LogPacket` represents the data which is sent over-the-wire to the host.
Nothing knows how to create instances of these types yet, but they'll be the backbone of the new PlaygroundLogger.
Implemented `LogPacket` initializers which produce values representing the kinds of things playground logging produces.
Hooked up the logger entrypoints to these `LogPacket` initializers; all entry points now generate the appropriate packets, though none of them send them anywhere.
Created an unimplemented `LogEntry` initializer which will create a `LogEntry` from an `Any`.
…r to send data to the host.

This is a C function pointer which takes a non-optional `NSData`.
`initializePlaygroundLogger()` follows legacy behavior and initializes this to `DVTSendPlaygroundLogData`, discovered via `dlsym`.
Other initialization methods (to be implemented in the future) will support initializing this to other values.
…ed this in the entrypoints.

As a result, the entrypoints are now fully implemented: they create a packet, encode that packet as `Data`, and then send that `Data` via `sendData`.
This all will crash due to unimplemented functions elsewhere in the stack, but as those are implemented this should all more-or-less come together.
Created a `LogEncoder` class which is capable of encoding the core log data types.
Created an unimplemented `LogEntry.encode(with:format:)` method which will soon encode a `LogEntry` into a given `LogEncoder` with the given `Format`.
Implemented the `LogPacket.encode(inFormat:)` function to properly encode its header and delegate to the `LogEntry.encode(with:format:)`.
Images, views, and some colors are not currently encoded and call `fatalError` should you try to encode one.
Implemented support for the following cases:

  - Types which implement `CustomPlaygroundRepresentable`
  - Types which implement `CustomPlaygroundQuickLookable`/`_DefaultCustomPlaygroundQuickLookable`
  - Fallback structural view for other types

There's also an unimplemented placeholder for types which implement the Objective-C method `-debugQuickLookObject`.
Aside from that, this is also an incomplete implementation as it does not handle errors very well -- for example, it just calls `fatalError` if converting from a `PlaygroundQuickLook` to a `LogEntry.OpaqueRepresentation` is not possible when it should instead throw an error.
Introduced the `OpaqueLogEntryRepresentation` protocol, which simply requires an `encode(into:usingFormat:)` method.
Made the `opaque` case of `LogEntry` take an `OpaqueLogEntryRepresentation` existential instead of an `OpaqueRepresentation` value.
Deprecated `LogEntry.OpaqueRepresentation`, but in lieu of transitioning anything off of it in this commit, simply made it conform to `OpaqueLogEntryRepresentation` and updated a couple sites accordingly.

Follow-on commits will introduce new types which conform to `OpaqueLogEntryRepresentation` and therefore remove `LogEntry.OpaqueRepresentation`.
…gEntryRepresentation` protocol (now typealiased as `LogEntry.OpaqueRepresentation`).

Implemented the new `OpaqueRepresentation` protocol on types which represent the current-supported opaque log entries. (With a couple notable exceptions: while NS/UIView and NS/UIImage purport to be supported, support for rendering images into log entries is not yet implemented and just calls `fatalError`.)
This is a more flexible approach which will make it easier to add support for other core types (for instance, support for logging CoreImage types directly).
Since it was no longer needed, deleted the old `OpaqueRepresentation` enum and its encoding support.
This will be used to implement opaque logging for core types in PlaygroundLogger, replacing implementations of `CustomPlaygroundQuickLookable` and `_DefaultCustomPlaygroundQuickLookable` in the standard library and overlay libraries.
… which conform to `LogEntry.OpaqueRepresentation`.

For types which opt-in to `CustomOpaqueLoggable`, this simply returns `self`, as in most cases an `OpaqueRepresentation`-conforming type wants to be its own opaque representation.
…brary.

This includes:

  - Bool
  - Float
  - Double
  - Int, UInt, and the specific-width Int and UInt types
  - String
  - Unsafe{,Mutable}{,Raw}Pointer
This includes:

  - Date/NSDate
  - NSAttributedString
  - NSNumber
  - NSRange
  - NSString
  - URL/NSURL
CGPoint, CGSize, CGRect, and CGColor implement `CustomOpaqueLoggable` now, using the default implementation as each of them are an opaque representation.
CGImage now implements both `CustomOpaqueLoggable` and `OpaqueImageRepresentable`, though its `OpaqueImageRepresentable` conformance is currently unimplemented (as are the other such conformances).
cwakamo and others added 27 commits February 23, 2018 21:04
Made `CustomOpaqueLoggable` and `PlaygroundQuickLook.opaqueRepresentation()` into throwing operations, as converting to an opaque representation may fail.
This means that creating a `LogEntry` may fail outright if the subject cannot provide its opaque representation.
Further, that means that both structural log entries and `LogPacket` may now contain error log entries representing cases when generating the opaque representation fails.
Note that this is not full error handling, as many errors occur during the encoding process rather than during `LogEntry` generation.
…nagement() to account for Swift changes.

Swift has changed how it generates these typenames, so we need to update accordingly.
…ing through to `loggingError`.

This means that logging shouldn't cause crashes, except in truly exceptional cases in the logger.
These errors are not quite propagated yet; that will come in a follow-up commit.
…opriately propagate error reasons.

This means that users will see some maybe-actionable information in the event of an error instead of a generic error message.
…d a TODO.

Pattern colors are not trivial to handle, so rather than crash in the face of pattern colors, throw an `encodingFailure` error.
Since nothing is marked as `unimplemented`, deleted `unimplemented` from PlaygroundLogger.
…ayConvertible` protocol.

Implementations of `CustomPlaygroundDisplayConvertible` are now checked; the instance returned by `playgroundDescription` is sent back through the primary initializer.
This includes a super-basic test that this functionality works.
…risky NSKeyedArchiver operations.

While most of our use of NSKeyedArchiver should be safe, encoding NSAttributedString, NSBezierPath, and UIBezierPath is potentially risky and may throw exceptions.
As a result, we need to perform this encoding in Objective-C source files compiled with `-fobjc-arc-exceptions` so we can correctly handle the exception and propagate the encoding failure as an error.
These are broken in some way, so I plan on recreating them in a follow-up commit.
…tomPlaygroundDisplayConvertible`.

`CustomPlaygroundDisplayConvertible` now exists in the standard library, so there's no need to provide this prototype version in PlaygroundRuntime.
This was only present for easy testing in PlaygroundLogger before the real protocol was created and available for use.
… optional.

In Swift versions where ImplicitlyUnwrappedOptional is a separate type from Optional, logging crashes as the runtime tries to implicitly unwrap the optional.
This test ensures that this behavior is covered appropriately.
…e name.

Since the point is to log it as if it weren't an optional, we shouldn't propagate the type name (which references the fact that it's an optional).
This is used by lldb via `@_silgen_name`, but if it's only internal then it's not available for use.
Unfortunately, summaries aren't just as simple as calling `String(describing:)`.
The legacy logger had some somewhat complex rules, but ultimately it fell back to the type name if nothing user-provided was available.
This reimplements that logic (save for truncation behavior).
…passing on some Macs.

This allows PlaygroundLogger's test suite to pass on macOS, iOS Simulator, and tvOS Simulator.
This was skipped in the previous logger implementation.
While it seems to pass locally fine, it fails in Swift CI. To unblock landing this branch, disable the test to maintain status quo with the previous logger implementation.
…ed-logger

Reimplemented PlaygroundLogger in a more straightforward, more maintainable manner, including support for SE-0198.
…encode empty images.

If a `NSView`, `NSImage`, or `UIImage` was zero-sized (e.g. 0x0), PlaygroundLogger would fail to encode this because it couldn't get a bitmap representation for the image.
Instead, handle this case specially, and instead of failing, send zero bytes of PNG data. Xcode renders this as an empty image, which is the expectation for this case.
This commit includes tests that empty views and images on all platforms generate image representations which can be encoded without throwing any errors.

This is an improvement over the legacy PlaygroundLogger implementation, which would render these plus empty `UIView` instances as if they were the string "empty image".

This addresses <rdar://problem/40207604>.
[PlaygroundLogger] Better handle encoding of empty images and views.
Offsetting indices only works for collections which implement `BidirectionalCollection`, while `suffix(_:)` is available for all collections.
This prevent crashes when e.g. generating a `LogEntry` for instances of `Set`.

This addresses <rdar://problem/39791397>.
[PlaygroundLogger] Use `prefix` and `suffix` when getting the first and last n children for structured log entries.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant