Skip to content

Commit 75bd780

Browse files
committed
Reflection: Add support for closure contexts to readMetadataFromInstance()
Also add end-to-end tests for this finally, and fix a bug in the SwiftReflectionTest library where we would give up on an module completely if it did not have a field metadata section. This is of course wrong if the module defines closures but not nominal types.
1 parent 4fccd2f commit 75bd780

File tree

4 files changed

+216
-24
lines changed

4 files changed

+216
-24
lines changed

include/swift/Reflection/ReflectionContext.h

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,40 @@ class ReflectionContext
119119
auto MetadataAddress = readMetadataFromInstance(ObjectAddress);
120120
if (!MetadataAddress.first)
121121
return nullptr;
122-
return getMetadataTypeInfo(MetadataAddress.second);
122+
123+
auto kind = this->readKindFromMetadata(MetadataAddress.second);
124+
if (!kind.first)
125+
return nullptr;
126+
127+
switch (kind.second) {
128+
case MetadataKind::Class:
129+
return getMetadataTypeInfo(MetadataAddress.second);
130+
131+
case MetadataKind::HeapLocalVariable: {
132+
auto CDAddr = this->readCaptureDescriptorFromMetadata(MetadataAddress.second);
133+
if (!CDAddr.first)
134+
return nullptr;
135+
136+
auto *CD = getBuilder().getCaptureDescriptor(CDAddr.second);
137+
if (CD == nullptr)
138+
return nullptr;
139+
140+
auto Info = getBuilder().getClosureContextInfo(*CD);
141+
142+
return getClosureContextInfo(ObjectAddress, Info);
143+
}
144+
145+
case MetadataKind::HeapGenericLocalVariable:
146+
// SIL @box type
147+
return nullptr;
148+
149+
case MetadataKind::ErrorObject:
150+
// ErrorProtocol boxed existential on non-Objective C runtime target
151+
return nullptr;
152+
153+
default:
154+
return nullptr;
155+
}
123156
}
124157

125158
bool

stdlib/private/SwiftReflectionTest/SwiftReflectionTest.swift

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -121,21 +121,19 @@ internal func getReflectionInfoForImage(atIndex i: UInt32) -> ReflectionInfo? {
121121
to: UnsafePointer<MachHeader>.self)
122122

123123
let imageName = _dyld_get_image_name(i)!
124-
if let fieldmd = getSectionInfo("__swift3_fieldmd", header) {
125-
let assocty = getSectionInfo("__swift3_assocty", header)
126-
let builtin = getSectionInfo("__swift3_builtin", header)
127-
let capture = getSectionInfo("__swift3_capture", header)
128-
let typeref = getSectionInfo("__swift3_typeref", header)
129-
let reflstr = getSectionInfo("__swift3_reflstr", header)
130-
return ReflectionInfo(imageName: String(validatingUTF8: imageName)!,
131-
fieldmd: fieldmd,
132-
assocty: assocty,
133-
builtin: builtin,
134-
capture: capture,
135-
typeref: typeref,
136-
reflstr: reflstr)
137-
}
138-
return nil
124+
let fieldmd = getSectionInfo("__swift3_fieldmd", header)
125+
let assocty = getSectionInfo("__swift3_assocty", header)
126+
let builtin = getSectionInfo("__swift3_builtin", header)
127+
let capture = getSectionInfo("__swift3_capture", header)
128+
let typeref = getSectionInfo("__swift3_typeref", header)
129+
let reflstr = getSectionInfo("__swift3_reflstr", header)
130+
return ReflectionInfo(imageName: String(validatingUTF8: imageName)!,
131+
fieldmd: fieldmd,
132+
assocty: assocty,
133+
builtin: builtin,
134+
capture: capture,
135+
typeref: typeref,
136+
reflstr: reflstr)
139137
}
140138

141139
internal func sendBytes<T>(from address: UnsafePointer<T>, count: Int) {
@@ -293,6 +291,9 @@ internal func reflect(instanceAddress: UInt, kind: InstanceKind) {
293291
}
294292

295293
/// Reflect a class instance.
294+
///
295+
/// This reflects the stored properties of the immediate class.
296+
/// The superclass is not (yet?) visited.
296297
public func reflect(object: AnyObject) {
297298
let address = unsafeAddress(of: object)
298299
let addressValue = unsafeBitCast(address, to: UInt.self)
@@ -301,6 +302,9 @@ public func reflect(object: AnyObject) {
301302

302303
/// Reflect any type at all by boxing it into an existential container (an `Any`)
303304
///
305+
/// Given a class, this will reflect the reference value, and not the contents
306+
/// of the instance. Use `reflect(object:)` for that.
307+
///
304308
/// This function serves to exercise the projectExistential function of the
305309
/// SwiftRemoteMirror API.
306310
///
@@ -358,6 +362,30 @@ public func reflect<T>(any: T) {
358362
anyPointer.deallocateCapacity(sizeof(Any.self))
359363
}
360364

365+
/// Reflect a closure context. The given function must be a Swift-native
366+
/// @convention(thick) function value.
367+
public func reflect(function: () -> ()) {
368+
struct ThickFunction {
369+
var function: () -> ()
370+
}
371+
372+
struct ThickFunctionParts {
373+
var function: UnsafePointer<Void>
374+
var context: Optional<UnsafePointer<Void>>
375+
}
376+
377+
let fn = UnsafeMutablePointer<ThickFunction>(
378+
allocatingCapacity: sizeof(ThickFunction.self))
379+
fn.initialize(with: ThickFunction(function: function))
380+
381+
let parts = unsafeBitCast(fn, to: UnsafePointer<ThickFunctionParts>.self)
382+
let contextPointer = unsafeBitCast(parts.pointee.context, to: UInt.self)
383+
384+
reflect(instanceAddress: contextPointer, kind: .Object)
385+
386+
fn.deallocateCapacity(sizeof(ThickFunction.self))
387+
}
388+
361389
/// Call this function to indicate to the parent that there are
362390
/// no more instances to look at.
363391
public func doneReflecting() {

validation-test/Reflection/existentials.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: rm -rf %t && mkdir -p %t
2-
// RUN: %target-build-swift -Xfrontend -enable-reflection-metadata -Xfrontend -enable-reflection-names -lswiftSwiftReflectionTest %s -o %t/example
3-
// RUN: %target-run %target-swift-reflection-test %t/example 2>&1 | FileCheck %s --check-prefix=CHECK-%target-ptrsize
2+
// RUN: %target-build-swift -Xfrontend -enable-reflection-metadata -Xfrontend -enable-reflection-names -lswiftSwiftReflectionTest %s -o %t/existentials
3+
// RUN: %target-run %target-swift-reflection-test %t/existentials 2>&1 | FileCheck %s --check-prefix=CHECK-%target-ptrsize
44
// REQUIRES: objc_interop
55

66
/*
@@ -42,7 +42,7 @@ reflect(any: mc)
4242
// CHECK-64: Reflecting an existential.
4343
// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}}
4444
// CHECK-64: Type reference:
45-
// CHECK-64: (bound_generic_class example.MyClass
45+
// CHECK-64: (bound_generic_class existentials.MyClass
4646
// CHECK-64: (struct Swift.Int)
4747
// CHECK-64: (struct Swift.Int))
4848
// CHECK-64: Type info:
@@ -51,7 +51,7 @@ reflect(any: mc)
5151
// CHECK-32: Reflecting an existential.
5252
// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}}
5353
// CHECK-32: Type reference:
54-
// CHECK-32: (bound_generic_class example.MyClass
54+
// CHECK-32: (bound_generic_class existentials.MyClass
5555
// CHECK-32: (struct Swift.Int)
5656
// CHECK-32: (struct Swift.Int))
5757
// CHECK-32: Type info:
@@ -64,7 +64,7 @@ reflect(any: smallStruct)
6464
// CHECK-64: Reflecting an existential.
6565
// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}}
6666
// CHECK-64: Type reference:
67-
// CHECK-64: (bound_generic_struct example.MyStruct
67+
// CHECK-64: (bound_generic_struct existentials.MyStruct
6868
// CHECK-64: (struct Swift.Int)
6969
// CHECK-64: (struct Swift.Int)
7070
// CHECK-64: (struct Swift.Int))
@@ -86,7 +86,7 @@ reflect(any: smallStruct)
8686
// CHECK-32: Reflecting an existential.
8787
// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}}
8888
// CHECK-32: Type reference:
89-
// CHECK-32: (bound_generic_struct example.MyStruct
89+
// CHECK-32: (bound_generic_struct existentials.MyStruct
9090
// CHECK-32: (struct Swift.Int)
9191
// CHECK-32: (struct Swift.Int)
9292
// CHECK-32: (struct Swift.Int))
@@ -113,7 +113,7 @@ reflect(any: largeStruct)
113113
// CHECK-64: Reflecting an existential.
114114
// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}}
115115
// CHECK-64: Type reference:
116-
// CHECK-64: (bound_generic_struct example.MyStruct
116+
// CHECK-64: (bound_generic_struct existentials.MyStruct
117117
// CHECK-64: (tuple
118118
// CHECK-64: (struct Swift.Int)
119119
// CHECK-64: (struct Swift.Int)
@@ -174,7 +174,7 @@ reflect(any: largeStruct)
174174
// CHECK-32: Reflecting an existential.
175175
// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}}
176176
// CHECK-32: Type reference:
177-
// CHECK-32: (bound_generic_struct example.MyStruct
177+
// CHECK-32: (bound_generic_struct existentials.MyStruct
178178
// CHECK-32: (tuple
179179
// CHECK-32: (struct Swift.Int)
180180
// CHECK-32: (struct Swift.Int)
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
// RUN: rm -rf %t && mkdir -p %t
2+
// RUN: %target-build-swift -Xfrontend -enable-reflection-metadata -Xfrontend -enable-reflection-names -lswiftSwiftReflectionTest %s -o %t/functions
3+
// RUN: %target-run %target-swift-reflection-test %t/functions 2>&1 | FileCheck %s --check-prefix=CHECK-%target-ptrsize
4+
// REQUIRES: objc_interop
5+
6+
/*
7+
This file pokes at the swift_reflection_infoForInstance() API
8+
of the SwiftRemoteMirror library.
9+
10+
It tests introspection of function closure contexts.
11+
12+
- See also: SwiftReflectionTest.reflect(function:)
13+
*/
14+
15+
import SwiftReflectionTest
16+
17+
func concrete(x: Int, y: Any) {
18+
reflect(function: {print(x)})
19+
// CHECK: Type reference:
20+
// CHECK-NEXT: (builtin Builtin.NativeObject)
21+
22+
// CHECK-32: Type info:
23+
// CHECK-32-NEXT: (closure_context size=16 alignment=4 stride=16 num_extra_inhabitants=0
24+
// CHECK-32-NEXT: (field offset=12
25+
// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0
26+
// CHECK-32-NEXT: (field offset=0
27+
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0)))))
28+
29+
// CHECK-64: Type info:
30+
// CHECK-64-NEXT: (closure_context size=24 alignment=8 stride=24 num_extra_inhabitants=0
31+
// CHECK-64-NEXT: (field offset=16
32+
// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0
33+
// CHECK-64-NEXT: (field offset=0
34+
// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0)))))
35+
36+
// FIXME: Need @box reflection -- here the context is a single boxed value
37+
reflect(function: {print(y)})
38+
// CHECK: Type reference:
39+
// CHECK-NEXT: (builtin Builtin.NativeObject)
40+
41+
// CHECK-32: Type info:
42+
// CHECK-32-NEXT: <null type info>
43+
44+
// CHECK-64: Type info:
45+
// CHECK-64-NEXT: <null type info>
46+
}
47+
48+
concrete(x: 10, y: true)
49+
50+
protocol P {}
51+
extension Int : P {}
52+
53+
class C {}
54+
55+
func generic<T : P, U, V : C>(x: T, y: U, z: V, i: Int) {
56+
reflect(function: {print(i)})
57+
// CHECK: Type reference:
58+
// CHECK-NEXT: (builtin Builtin.NativeObject)
59+
60+
// CHECK-32: Type info:
61+
// CHECK-32-NEXT: (closure_context size=16 alignment=4 stride=16 num_extra_inhabitants=0
62+
// CHECK-32-NEXT: (field offset=12
63+
// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0
64+
// CHECK-32-NEXT: (field offset=0
65+
// CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0)))))
66+
67+
// CHECK-64: Type info:
68+
// CHECK-64-NEXT: (closure_context size=24 alignment=8 stride=24 num_extra_inhabitants=0
69+
// CHECK-64-NEXT: (field offset=16
70+
// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0
71+
// CHECK-64-NEXT: (field offset=0
72+
// CHECK-64-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0)))))
73+
74+
reflect(function: {print(x); print(y); print(z)})
75+
// CHECK: Type reference:
76+
// CHECK-NEXT: (builtin Builtin.NativeObject)
77+
78+
// CHECK-32: Type info:
79+
// CHECK-32-NEXT: (closure_context size=24 alignment=4 stride=24 num_extra_inhabitants=0
80+
// CHECK-32-NEXT: (field offset=12
81+
// CHECK-32-NEXT: (reference kind=strong refcounting=native))
82+
// CHECK-32-NEXT: (field offset=16
83+
// CHECK-32-NEXT: (reference kind=strong refcounting=native))
84+
// CHECK-32-NEXT: (field offset=20
85+
// CHECK-32-NEXT: (reference kind=strong refcounting=native)))
86+
87+
// CHECK-64: Type info:
88+
// CHECK-64-NEXT: (closure_context size=40 alignment=8 stride=40 num_extra_inhabitants=0
89+
// CHECK-64-NEXT: (field offset=16
90+
// CHECK-64-NEXT: (reference kind=strong refcounting=native))
91+
// CHECK-64-NEXT: (field offset=24
92+
// CHECK-64-NEXT: (reference kind=strong refcounting=native))
93+
// CHECK-64-NEXT: (field offset=32
94+
// CHECK-64-NEXT: (reference kind=strong refcounting=native)))
95+
}
96+
97+
generic(x: 10, y: "", z: C(), i: 101)
98+
99+
class GC<A, B, C> {}
100+
101+
func genericWithSources<A, B, C>(a: A, b: B, c: C, gc: GC<A, B, C>) {
102+
reflect(function: {print(a); print(b); print(c); print(gc)})
103+
// CHECK: Type reference:
104+
// CHECK-NEXT: (builtin Builtin.NativeObject)
105+
106+
// CHECK-32: Type info:
107+
// CHECK-32-NEXT: (closure_context size=28 alignment=4 stride=28 num_extra_inhabitants=0
108+
// CHECK-32-NEXT: (field offset=12
109+
// CHECK-32-NEXT: (reference kind=strong refcounting=native))
110+
// CHECK-32-NEXT: (field offset=16
111+
// CHECK-32-NEXT: (reference kind=strong refcounting=native))
112+
// CHECK-32-NEXT: (field offset=20
113+
// CHECK-32-NEXT: (reference kind=strong refcounting=native))
114+
// CHECK-32-NEXT: (field offset=24
115+
// CHECK-32-NEXT: (reference kind=strong refcounting=native)))
116+
117+
// CHECK-64: Type info:
118+
// CHECK-64-NEXT: (closure_context size=48 alignment=8 stride=48 num_extra_inhabitants=0
119+
// CHECK-64-NEXT: (field offset=16
120+
// CHECK-64-NEXT: (reference kind=strong refcounting=native))
121+
// CHECK-64-NEXT: (field offset=24
122+
// CHECK-64-NEXT: (reference kind=strong refcounting=native))
123+
// CHECK-64-NEXT: (field offset=32
124+
// CHECK-64-NEXT: (reference kind=strong refcounting=native))
125+
// CHECK-64-NEXT: (field offset=40
126+
// CHECK-64-NEXT: (reference kind=strong refcounting=native)))
127+
}
128+
129+
genericWithSources(a: (), b: ((), ()), c: ((), (), ()), gc: GC<(), ((), ()), ((), (), ())>())
130+
131+
doneReflecting()

0 commit comments

Comments
 (0)