Files
swift-mirror/stdlib/core/Reflection.swift
Enrico Granata 0134c6d197 Allow user-defined Swift classes to implement our API for QuickLooks, and have the result of that API be picked up by the Mirrors
This is our public API for how quicklooks work in the debugger, and the plan is to have this same API work in playgrounds as well

Fixes rdar://17023157


Swift SVN r18609
2014-05-24 01:00:46 +00:00

454 lines
14 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
/// A protocol that produces a reflection interface for a value.
protocol Reflectable {
// The runtime has inappropriate knowledge of this protocol and how its
// witness tables are laid out. Changing this protocol requires a
// corresponding change to Reflection.cpp.
/// Get the mirror that reflects this object.
func getMirror() -> Mirror
}
/// A unique identifier for a class instance. This can be used by reflection
/// clients to recognize cycles in the object graph.
///
/// In Swift, only class instances have unique identities. There is no notion
/// of identity for structs, enums, or tuples.
struct ObjectIdentifier : Hashable {
let value: Builtin.RawPointer
func uintValue() -> UInt {
return UInt(Builtin.ptrtoint_Word(value))
}
// FIXME: Better hashing algorithm
var hashValue: Int {
return Int(Builtin.ptrtoint_Word(value))
}
init(_ x: AnyObject) {
self.value = reinterpretCast(x)
}
}
func ==(x: ObjectIdentifier, y: ObjectIdentifier) -> Bool {
return Bool(Builtin.cmp_eq_RawPointer(x.value, y.value))
}
/// The sum of types that can be used as a quick look representation.
///
/// This type must be binary-compatible with the 'QuickLookObject' struct in
/// stdlib/Runtime/Reflection.mm, and 'QuickLookObject?' must be binary
/// compatible with 'OptionalQuickLookObject' from the same.
///
/// NB: This type is somewhat carefully laid out to *suppress* enum layout
/// optimization so that it is easier to manufacture in the C++ runtime
/// implementation.
enum QuickLookObject {
/// Plain text.
case Text(String)
/// An integer numeric value.
case Int(Int64)
/// An unsigned integer numeric value.
case UInt(UInt64)
/// A floating-point numeric value.
case Float(Double)
/// An image.
/// FIXME: Uses an Any to avoid coupling a particular Cocoa type.
case Image(Any)
/// A sound.
/// FIXME: Uses an Any to avoid coupling a particular Cocoa type.
case Sound(Any)
/// A color.
/// FIXME: Uses an Any to avoid coupling a particular Cocoa type.
case Color(Any)
/// A bezier path.
/// FIXME: Uses an Any to avoid coupling a particular Cocoa type.
case BezierPath(Any)
/// An attributed string.
/// FIXME: Uses an Any to avoid coupling a particular Cocoa type.
case AttributedString(Any)
/// A rectangle
/// Uses explicit coordinates to avoid coupling a particular Cocoa type.
case Rectangle(Double,Double,Double,Double)
/// A point
/// Uses explicit coordinates to avoid coupling a particular Cocoa type.
case Point(Double,Double)
/// A size
/// Uses explicit coordinates to avoid coupling a particular Cocoa type.
case Size(Double,Double)
/// A logical value
case Logical(Bool)
/// A range
/// Uses explicit values to avoid coupling a particular Cocoa type.
case Range(UInt64, UInt64)
/// A GUI view
/// Uses an Any to avoid coupling a particular Cocoa type.
case View(Any)
/// A graphical sprite
/// Uses an Any to avoid coupling a particular Cocoa type.
case Sprite(Any)
/// A Uniform Resource Locator
case URL(String)
/// Raw data that has already been encoded in a format the IDE understands.
case _Raw(UInt8[], String)
}
/// How children of this value should be presented in the IDE.
enum MirrorDisposition {
/// As a struct.
case Struct
/// As a class.
case Class
/// As an enum.
case Enum
/// As a tuple.
case Tuple
/// As a miscellaneous aggregate with a fixed set of children.
case Aggregate
/// As a container that is accessed by index.
case IndexContainer
/// As a container that is accessed by key.
case KeyContainer
/// As a container that represents membership of its values.
case MembershipContainer
/// As a miscellaneous container with a variable number of children.
case Container
/// An Optional which can have either zero or one children.
case Optional
}
/// A protocol that provides a reflection interface to an underlying value.
protocol Mirror {
/// Copy the value out as an Any.
var value: Any { get }
/// Get the type of the value.
var valueType: Any.Type { get }
/// Get the unique identifier for this value, if it has one.
/// Always returns Some value for class instances, and always returns None
/// for value types.
var objectIdentifier: ObjectIdentifier? { get }
/// Get the number of logical children this value has.
var count: Int { get }
/// Get a mirror for one of this value's children.
///
/// Returns a pair of the child's name and its mirror.
subscript(i: Int) -> (String, Mirror) { get }
/// Get a string description of this value.
var summary: String { get }
/// Get a rich representation of this value for the IDE, if it has one.
var quickLookObject: QuickLookObject? { get }
/// Get the disposition of the value.
var disposition: MirrorDisposition { get }
}
/// An entry point that can be called from C++ code to get the summary string
/// for an arbitrary object. The memory pointed to by "out" is initialized with
/// the summary string.
@asmname("swift_getSummary") func _getSummary<T>(out: UnsafePointer<String>,
x: T) {
out.initialize(reflect(x).summary)
}
/// Produce a mirror for any value. If the value's type conforms to Reflectable,
/// invoke its getMirror() method; otherwise, fall back to an implementation
/// in the runtime that structurally reflects values of any type.
@asmname("swift_reflectAny") func reflect<T>(x: T) -> Mirror
/// Unsafely produce a mirror for a value in memory whose lifetime is
/// guaranteed by holding a strong reference to a heap object.
/// This lets containers with heap storage vend mirrors for their elements
/// without unnecessary copying of the underlying value.
@asmname("swift_unsafeReflectAny") func unsafeReflect<T>(
owner: Builtin.NativeObject,
ptr: UnsafePointer<T>
) -> Mirror
/// Dump an object's contents using its mirror to the specified output stream.
func dump<T, TargetStream : OutputStream>(
x: T, name: String? = nil, indent: Int = 0,
maxDepth: Int = .max, maxItems: Int = .max,
inout targetStream: TargetStream
) -> T {
var maxItemCounter = maxItems
var visitedItems = Dictionary<ObjectIdentifier, Int>()
_dumpWithMirror(reflect(x), name, indent, maxDepth,
&maxItemCounter, &visitedItems, &targetStream)
return x
}
/// Dump an object's contents using its mirror to standard output.
func dump<T>(x: T, name: String? = nil, indent: Int = 0,
maxDepth: Int = .max, maxItems: Int = .max) -> T {
var stdoutStream = _Stdout()
return dump(x, name: name, indent: indent, maxDepth: maxDepth,
maxItems: maxItems, &stdoutStream)
}
/// Dump an object's contents using a mirror. User code should use dump().
func _dumpWithMirror<TargetStream : OutputStream>(
mirror: Mirror, name: String?, indent: Int, maxDepth: Int,
inout maxItemCounter: Int,
inout visitedItems: Dictionary<ObjectIdentifier, Int>,
inout targetStream: TargetStream
) {
if maxItemCounter <= 0 { return }
--maxItemCounter
for _ in 0..indent { print(" ") }
let count = mirror.count
let bullet = count == 0 ? "-"
: maxDepth <= 0 ? "" : ""
print("\(bullet) ", &targetStream)
if let nam = name {
print("\(nam): ", &targetStream)
}
print(mirror.summary)
if let id = mirror.objectIdentifier {
if let previous = visitedItems[id] {
println(" #\(previous)", &targetStream)
return
}
let identifier = visitedItems.count
visitedItems[id] = identifier
print(" #\(identifier)", &targetStream)
}
println("", &targetStream)
if maxDepth <= 0 { return }
for i in 0..count {
if maxItemCounter <= 0 {
for _ in 0..(indent+4) { print(" ") }
let remainder = count - i
print("(\(remainder)", &targetStream)
if i > 0 { print(" more", &targetStream) }
if remainder == 1 {
println(" child)", &targetStream)
} else {
println(" children)", &targetStream)
}
return
}
let (name, child) = mirror[i]
_dumpWithMirror(child, name, indent + 2, maxDepth - 1,
&maxItemCounter, &visitedItems, &targetStream)
}
}
// -- Mirror implementations for basic data types
func _formatNumChildren(count: Int) -> String {
if count == 1 {
return " (has 1 child)"
} else {
return " (has \(count) children)"
}
}
/// A mirror for a value that is represented as a simple value with no
/// children.
struct _LeafMirror<T>: Mirror {
let _value: T
let summaryFunction: T -> String
let quickLookFunction: T -> QuickLookObject?
init(_ value: T, _ summaryFunction: T -> String,
_ quickLookFunction: T -> QuickLookObject?) {
self._value = value
self.summaryFunction = summaryFunction
self.quickLookFunction = quickLookFunction
}
var value: Any { return _value }
var valueType: Any.Type { return value.dynamicType }
var objectIdentifier: ObjectIdentifier? { return nil }
var count: Int { return 0 }
subscript(i: Int) -> (String, Mirror) { _preconditionFailure("no children") }
var summary: String { return summaryFunction(_value) }
var quickLookObject: QuickLookObject? { return quickLookFunction(_value) }
var disposition: MirrorDisposition { return .Aggregate }
}
// -- Implementation details for the runtime's Mirror implementation
@asmname("swift_MagicMirrorData_summary")
func swift_MagicMirrorData_summaryImpl(metadata: Any.Type, result: UnsafePointer<String>)
struct _MagicMirrorData {
let owner: Builtin.NativeObject
let ptr: Builtin.RawPointer
let metadata: Any.Type
var value: Any {
@asmname("swift_MagicMirrorData_value") get
}
var valueType: Any.Type {
@asmname("swift_MagicMirrorData_valueType") get
}
var objcValue: Any {
@asmname("swift_MagicMirrorData_objcValue") get
}
var objcValueType: Any.Type {
@asmname("swift_MagicMirrorData_objcValueType") get
}
var summary: String {
var resultPtr = UnsafePointer<String>.alloc(1)
swift_MagicMirrorData_summaryImpl(metadata, resultPtr)
let result = resultPtr.memory
resultPtr.dealloc(1)
return result
}
func _loadValue<T>() -> T {
return Builtin.load(ptr) as T
}
}
struct _OpaqueMirror: Mirror {
let data: _MagicMirrorData
var value: Any { return data.value }
var valueType: Any.Type { return data.valueType }
var objectIdentifier: ObjectIdentifier? { return nil }
var count: Int { return 0 }
subscript(i: Int) -> (String, Mirror) { _preconditionFailure("no children") }
var summary: String { return data.summary }
var quickLookObject: QuickLookObject? { return nil }
var disposition: MirrorDisposition { return .Aggregate }
}
struct _TupleMirror: Mirror {
let data: _MagicMirrorData
var value: Any { return data.value }
var valueType: Any.Type { return data.valueType }
var objectIdentifier: ObjectIdentifier? { return nil }
var count: Int {
@asmname("swift_TupleMirror_count") get
}
subscript(i: Int) -> (String, Mirror) {
@asmname("swift_TupleMirror_subscript") get
}
var summary: String { return "(\(count) elements)" }
var quickLookObject: QuickLookObject? { return nil }
var disposition: MirrorDisposition { return .Tuple }
}
struct _StructMirror: Mirror {
let data: _MagicMirrorData
var value: Any { return data.value }
var valueType: Any.Type { return data.valueType }
var objectIdentifier: ObjectIdentifier? { return nil }
var count: Int {
@asmname("swift_StructMirror_count") get
}
subscript(i: Int) -> (String, Mirror) {
@asmname("swift_StructMirror_subscript") get
}
var summary: String {
return "\(_stdlib_getTypeName(value))\(_formatNumChildren(count))"
}
var quickLookObject: QuickLookObject? { return nil }
var disposition: MirrorDisposition { return .Struct }
}
@asmname("swift_ClassMirror_count")
func _getClassCount(_MagicMirrorData) -> Int
@asmname("swift_ClassMirror_subscript")
func _getClassChild(Int, _MagicMirrorData) -> (String, Mirror)
@asmname("swift_ClassMirror_quickLookObject")
func _getClassQuickLookObject(data: _MagicMirrorData) -> QuickLookObject?
struct _ClassMirror: Mirror {
let data: _MagicMirrorData
var value: Any { return data.value }
var valueType: Any.Type { return data.valueType }
var objectIdentifier: ObjectIdentifier? {
return data._loadValue() as ObjectIdentifier
}
var count: Int {
return _getClassCount(data)
}
subscript(i: Int) -> (String, Mirror) {
return _getClassChild(i, data)
}
var summary: String {
return "\(_stdlib_getTypeName(value))\(_formatNumChildren(count))"
}
var quickLookObject: QuickLookObject? {
return _getClassQuickLookObject(data)
}
var disposition: MirrorDisposition { return .Class }
}
struct _ClassSuperMirror: Mirror {
let data: _MagicMirrorData
var value: Any { return data.value }
var valueType: Any.Type { return data.valueType }
// Suppress the value identifier for super mirrors.
var objectIdentifier: ObjectIdentifier? {
return nil
}
var count: Int {
return _getClassCount(data)
}
subscript(i: Int) -> (String, Mirror) {
return _getClassChild(i, data)
}
var summary: String {
return "\(_stdlib_getTypeName(value))\(_formatNumChildren(count))"
}
var quickLookObject: QuickLookObject? { return nil }
var disposition: MirrorDisposition { return .Class }
}