Skip to content
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

Reimplemented PlaygroundLogger in a more straightforward, more maintainable manner, including support for SE-0198. #23

Merged
merged 75 commits into from
Jul 5, 2018

Conversation

cwakamo
Copy link
Member

@cwakamo cwakamo commented Feb 25, 2018

The structure of PlaygroundLogger is a bit more straightforward: logging generates LogPacket values which contain LogEntry values. This is done by checking a series of protocol conformances:

  • CustomPlaygroundDisplayConvertible, the new public protocol in the standard library for customizing logger behavior
  • CustomOpaqueLoggable, a PlaygroundLogger-internal protocol for types which PlaygroundLogger knows how to convert to an "opaque representation"
  • CustomPlaygroundQuickLookable and _DefaultCustomPlaygroundQuickLookable, the legacy protocols in the standard library

PlaygroundLogger also checks to see if the Objective-C method -debugQuickLookObject is implemented; if it is, it calls that method and uses the result for logging. If nothing special is implemented, PlaygroundLogger then uses Mirror to generate a structural LogEntry (as opposed to an opaque LogEntry).

As stated above, this includes support for SE-0198: the new PlaygroundLogger now honors CustomPlaygroundDisplayConvertible conformances.

The new PlaygroundLogger implementation also includes some experimental, WIP changes to how the PlaygroundLogger framework is meant to be used. (The idea is that initialization occurs via a function exposed using @_cdecl so that a stub process can initialize PlaygroundLogger with a particular client version and with a sendData function, thereby allowing two things: customization of log entries based on client version, and support for ultimately removing the sendData function from the playground transform.) The legacy entry points used by lldb for playground execution remain available in this new implementation, however.

This PR also introduces a PlaygroundRuntime framework in the "Experimental" subdirectory which is intended to be a single framework which provides the builtin functions required by the playground transform and the PC macro. (These are implemented as nonoptional thin function references; this minimizes overhead and would allow an initialization process to connect PlaygroundLogger to PlaygroundRuntime, which then allows PlaygroundLogger to stop exporting the playground_log_* functions as ABI to lldb.)

The legacy PlaygroundLogger implementation remains available in the "Legacy" subdirectory, but it'll be removed in a follow-up PR.

This resolves <rdar://problem/37765445> and <rdar://problem/37421663>.

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).
…er.deallocate(capacity:)` in the legacy test code.
PlaygroundLogger needs to be tested on both iOS and tvOS, so the commit introduces test bundle and test host app targets for those platforms, adding to the test bundle already present for macOS.
It's important to note that, due to signing limitations, this testing only works in the simulator and not on real devices.
This commit also introduces iOS and tvOS PlaygroundLogger schemes which run the appropriate tests.
… sprites.

We now send PNG data for images and views, plus whatever SpriteKit gives us via _copyImageData.
Note that this does not yet handle recursive calls correctly, so in a playground environment this would likely try to log `self` when implicitly calling `-drawRect:` on a view that's being logged.
…he number of children logged.

The defaults are to log up to the first 10,000 children of "aggregates" (i.e. classes, structs, etc.) and up to the first 80 + last 20 of "containers" (i.e. arrays, dictionaries, sets, etc.).
This matches the behavior of the legacy PlaygroundLogger.
This commit includes introducing new tests that these policies are applied correctly.
…ntries.

This pulls over behavior matching the legacy PlaygroundLogger, which capped log entries at a maximum depth of 2.
This includes commits for a small handful of cases. More to be added later.
This is intended as a placeholder for all of the places which should generate proper errors once error handling/generation is implemented.
This makes it easier to see what's actually unimplemented, what actually should call fatalError, and what actually should report proper errors to the user.
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.
@cwakamo cwakamo merged commit 063a4b0 into apple:master Jul 5, 2018
@cwakamo cwakamo deleted the runtime-framework-and-improved-logger branch July 5, 2018 23:04
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.

None yet

1 participant