Skip to content

Commit 594adbf

Browse files
committed
Refactor glue file hierarchy
Should increase performance (inlining due to Swift being able to see concrete _Glue types) while hopefully simplifying Orion's code conceptually (fewer existentials, single "bridge" point etc)
1 parent c042a52 commit 594adbf

16 files changed

+945
-821
lines changed

.jazzy.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ custom_categories:
3333
- name: All Hooks
3434
children:
3535
- AnyHook
36-
- AnyHookBase
3736
- name: Groups
3837
children:
3938
- HookGroup

Orion.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
56AFA08B25A3CE5F00884E2D /* dealloc.h in Headers */ = {isa = PBXBuildFile; fileRef = 56AFA08A25A3CE5F00884E2D /* dealloc.h */; settings = {ATTRIBUTES = (Public, ); }; };
3131
56AFA08D25A3CE6500884E2D /* dealloc.m in Sources */ = {isa = PBXBuildFile; fileRef = 56AFA08C25A3CE6500884E2D /* dealloc.m */; };
3232
56AFA08F25A3CE8B00884E2D /* ClassHook+Deinit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AFA08E25A3CE8A00884E2D /* ClassHook+Deinit.swift */; };
33+
56D50FAE25F11DEE00AE26FA /* ClassHook+Glue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D50FAD25F11DEE00AE26FA /* ClassHook+Glue.swift */; };
34+
56D50FB125F11DF500AE26FA /* FunctionHook+Glue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D50FB025F11DF500AE26FA /* FunctionHook+Glue.swift */; };
3335
56D5B37F258F6F9D00F3DB6E /* ErrorHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D5B37E258F6F9D00F3DB6E /* ErrorHandling.swift */; };
3436
56D9F14925C4940900EDB315 /* LazyAtomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D9F14825C4940900EDB315 /* LazyAtomic.swift */; };
3537
/* End PBXBuildFile section */
@@ -68,6 +70,8 @@
6870
56AFA08A25A3CE5F00884E2D /* dealloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dealloc.h; sourceTree = "<group>"; };
6971
56AFA08C25A3CE6500884E2D /* dealloc.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = dealloc.m; sourceTree = "<group>"; };
7072
56AFA08E25A3CE8A00884E2D /* ClassHook+Deinit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ClassHook+Deinit.swift"; sourceTree = "<group>"; };
73+
56D50FAD25F11DEE00AE26FA /* ClassHook+Glue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ClassHook+Glue.swift"; sourceTree = "<group>"; };
74+
56D50FB025F11DF500AE26FA /* FunctionHook+Glue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FunctionHook+Glue.swift"; sourceTree = "<group>"; };
7175
56D5B37E258F6F9D00F3DB6E /* ErrorHandling.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorHandling.swift; sourceTree = "<group>"; };
7276
56D9F14825C4940900EDB315 /* LazyAtomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LazyAtomic.swift; sourceTree = "<group>"; };
7377
/* End PBXFileReference section */
@@ -195,10 +199,12 @@
195199
568974A4252BBA15006F48E9 /* Backend.swift */,
196200
5689749C252BBA15006F48E9 /* ClassHook.swift */,
197201
56AFA08E25A3CE8A00884E2D /* ClassHook+Deinit.swift */,
202+
56D50FAD25F11DEE00AE26FA /* ClassHook+Glue.swift */,
198203
568974A0252BBA15006F48E9 /* ClassHook+Super.swift */,
199204
5689749D252BBA15006F48E9 /* Dynamic.swift */,
200205
56D5B37E258F6F9D00F3DB6E /* ErrorHandling.swift */,
201206
5689749E252BBA15006F48E9 /* FunctionHook.swift */,
207+
56D50FB025F11DF500AE26FA /* FunctionHook+Glue.swift */,
202208
56080B5B25C1F8BE005803BC /* Group.swift */,
203209
568974A8252BBA15006F48E9 /* Hook.swift */,
204210
5689749F252BBA15006F48E9 /* InternalBackend.swift */,
@@ -397,6 +403,8 @@
397403
568974B5252BBA15006F48E9 /* ReadWriteLock.swift in Sources */,
398404
56D9F14925C4940900EDB315 /* LazyAtomic.swift in Sources */,
399405
568974C4252BBA4F006F48E9 /* super.m in Sources */,
406+
56D50FB125F11DF500AE26FA /* FunctionHook+Glue.swift in Sources */,
407+
56D50FAE25F11DEE00AE26FA /* ClassHook+Glue.swift in Sources */,
400408
568974AA252BBA15006F48E9 /* ClassHook.swift in Sources */,
401409
568974B1252BBA15006F48E9 /* Property.swift in Sources */,
402410
568974B3252BBA15006F48E9 /* MutexLock.swift in Sources */,

Sources/Orion/Backend.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public protocol Backend {
6060
}
6161

6262
extension Backend {
63-
func activate(hooks: [_AnyGlueHook.Type]) {
63+
func activate(hooks: [_GlueAnyHook.Type]) {
6464
let hooksToActivate = hooks.filter { $0.hookWillActivate() }
6565
apply(descriptors: hooksToActivate.flatMap { $0.activate() })
6666
hooksToActivate.forEach { $0.hookDidActivate() }

Sources/Orion/ClassHook+Deinit.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ public enum DeinitPolicy {
1515
}
1616

1717
/// :nodoc:
18-
extension _ClassHookBuilder {
18+
extension _GlueClassHookBuilder {
1919
private static let deallocSelector = NSSelectorFromString("dealloc")
2020

2121
public typealias Deinitializer = @convention(c) (Any, Selector) -> Void
2222

2323
// for some reason using `@escaping Deinitializer` instead of `Code`
2424
// works in SPM but not with the binary framework (maybe a swiftinterface
2525
// bug or library evolution thing?)
26-
public mutating func addDeinitializer<T: _GlueClassHook, Code>(
26+
public mutating func addDeinitializer<T: ClassHookProtocol, Code>(
2727
to classHook: T.Type,
2828
getOrig: @escaping () -> Deinitializer,
2929
setOrig: @escaping (Code) -> Void
@@ -60,7 +60,7 @@ extension _ClassHookBuilder {
6060
}
6161

6262
/// :nodoc:
63-
extension _GlueClassHook {
63+
extension _GlueClassHookTrampoline {
6464
public func deinitOrigError(file: StaticString = #file, line: UInt = #line) -> Never {
6565
orionError("Do not call `orig.deinitializer()`.", file: file, line: line)
6666
}

Sources/Orion/ClassHook+Glue.swift

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import Foundation
2+
3+
/// Internal storage associated with a `ClassHook`. Do not use this yourself.
4+
///
5+
/// :nodoc:
6+
public final class _GlueClassHookStorage {
7+
let hookType: AnyClass
8+
@LazyAtomic private(set) var targetType: AnyObject.Type
9+
@LazyAtomic private(set) var group: HookGroup
10+
11+
// additional fields may be added here in the future
12+
13+
init(
14+
hookType: AnyClass,
15+
loadTargetType: @escaping () -> AnyObject.Type,
16+
loadGroup: @escaping () -> HookGroup
17+
) {
18+
self.hookType = hookType
19+
_targetType = LazyAtomic(wrappedValue: loadTargetType())
20+
_group = LazyAtomic(wrappedValue: loadGroup())
21+
}
22+
}
23+
24+
/// A placeholder class hook glue type. Do not use this yourself.
25+
///
26+
/// This type is the default value for the `ClassHookProtocol._Glue` constraint,
27+
/// used to satisfy the compiler until the actual glue is provided.
28+
///
29+
/// :nodoc:
30+
public enum _GlueClassHookPlaceholder<HookType: ClassHookProtocol>: _GlueClassHook {
31+
public typealias OrigType = HookType
32+
public typealias SuprType = HookType
33+
34+
public static var storage: _GlueClassHookStorage { error() }
35+
public static func activate(withClassHookBuilder builder: inout _GlueClassHookBuilder) { error() }
36+
37+
private static func error() -> Never {
38+
orionError("Placeholder class hook used. Has the glue file been compiled?")
39+
}
40+
}
41+
42+
/// A marker protocol to which `_GlueClassHook`'s orig/supr trampolines conform. Do
43+
/// not use this yourself.
44+
///
45+
/// :nodoc:
46+
public protocol _GlueClassHookTrampoline: ClassHookProtocol {}
47+
48+
/// A helper type used in the glue file for applying class hooks. Do not
49+
/// use this directly.
50+
///
51+
/// :nodoc:
52+
public struct _GlueClassHookBuilder {
53+
let target: AnyClass
54+
55+
var descriptors: [HookDescriptor] = []
56+
57+
public mutating func addHook<Code>(
58+
_ sel: Selector,
59+
_ replacement: Code,
60+
isClassMethod: Bool,
61+
saveOrig: @escaping (Code) -> Void
62+
) {
63+
let cls: AnyClass = isClassMethod ? object_getClass(target)! : target
64+
descriptors.append(
65+
.method(cls: cls, sel: sel, replacement: unsafeBitCast(replacement, to: UnsafeMutableRawPointer.self)) {
66+
saveOrig(unsafeBitCast($0, to: Code.self))
67+
}
68+
)
69+
}
70+
}
71+
72+
/// A concrete class hook, implemented in the glue file. Do not use
73+
/// this directly.
74+
///
75+
/// :nodoc:
76+
public protocol _GlueClassHook: _GlueAnyHook {
77+
associatedtype HookType: ClassHookProtocol
78+
associatedtype OrigType: ClassHookProtocol where OrigType.Target == HookType.Target
79+
associatedtype SuprType: ClassHookProtocol where SuprType.Target == HookType.Target
80+
81+
static var storage: _GlueClassHookStorage { get }
82+
static func activate(withClassHookBuilder builder: inout _GlueClassHookBuilder)
83+
}
84+
85+
/// :nodoc:
86+
extension _GlueClassHook {
87+
public static func addMethod<Code>(_ selector: Selector, _ implementation: Code, isClassMethod: Bool) {
88+
let methodDescription = { "\(isClassMethod ? "+" : "-")[\(self) \(selector)]" }
89+
guard let method = (isClassMethod ? class_getClassMethod : class_getInstanceMethod)(HookType.self, selector)
90+
else { orionError("Could not find method \(methodDescription())") }
91+
guard let types = method_getTypeEncoding(method)
92+
else { orionError("Could not get method signature for \(methodDescription())") }
93+
let cls: AnyClass = isClassMethod ? object_getClass(HookType.target)! : HookType.target
94+
guard class_addMethod(cls, selector, unsafeBitCast(implementation, to: IMP.self), types)
95+
else { orionError("Failed to add method \(methodDescription())") }
96+
}
97+
98+
public static func activate() -> [HookDescriptor] {
99+
var classHookBuilder = _GlueClassHookBuilder(target: HookType.target)
100+
activate(withClassHookBuilder: &classHookBuilder)
101+
return classHookBuilder.descriptors
102+
}
103+
104+
public static var groupType: HookGroup.Type {
105+
HookType.Group.self
106+
}
107+
108+
public static func hookWillActivate() -> Bool {
109+
HookType.hookWillActivate()
110+
}
111+
112+
public static func hookDidActivate() {
113+
HookType.hookDidActivate()
114+
}
115+
116+
public static func initializeStorage() -> _GlueClassHookStorage {
117+
// this gives us the type of the user's hook rather than
118+
// our concrete subclass
119+
_GlueClassHookStorage(
120+
hookType: HookType.self,
121+
loadTargetType: HookType.initializeTargetType,
122+
loadGroup: HookType.loadGroup
123+
)
124+
}
125+
}
126+
127+
extension SubclassMode {
128+
fileprivate func subclassName(withType type: AnyClass) -> String? {
129+
switch self {
130+
case .none:
131+
return nil
132+
case .createSubclass:
133+
return "OrionSubclass.\(NSStringFromClass(type))"
134+
case .createSubclassNamed(let name):
135+
return name
136+
}
137+
}
138+
}
139+
140+
/// :nodoc:
141+
extension ClassHookProtocol {
142+
// since `target` is referred to in `activate()`, this will deterministically be called
143+
// when a class hook is activated.
144+
fileprivate static func initializeTargetType() -> Target.Type {
145+
let targetName = self.targetName // only call getter once
146+
let baseTarget = targetName.isEmpty ? Target.self : Dynamic(targetName).as(type: Target.self)
147+
148+
let target: Target.Type
149+
if let subclassName = subclassMode.subclassName(withType: _Glue.storage.hookType) {
150+
guard let pair: AnyClass = objc_allocateClassPair(baseTarget, subclassName, 0)
151+
else { orionError("Could not allocate subclass for \(self)") }
152+
objc_registerClassPair(pair)
153+
guard let _target = pair as? Target.Type
154+
else { orionError("Allocated invalid subclass for \(self)") }
155+
target = _target
156+
} else {
157+
target = baseTarget
158+
}
159+
160+
protocols.forEach {
161+
guard class_addProtocol(target, $0)
162+
else { orionError("Could not add protocol \($0) to \(target)") }
163+
}
164+
return target
165+
}
166+
}

Sources/Orion/ClassHook+Super.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ enum MessageSendSuperType {
4747
}
4848

4949
/// :nodoc:
50-
extension _GlueClassHook {
50+
extension _GlueClassHookTrampoline {
5151
private static func callSuper<ReturnType, MessageType>(
5252
_ type: MessageType.Type,
5353
receiver: Any,

0 commit comments

Comments
 (0)