Skip to content

Commit f1fb296

Browse files
authored
Merge pull request #28 from p-x9/feature/improve-key-implementation
Improve key implementation
2 parents 6bde180 + e775fba commit f1fb296

File tree

9 files changed

+324
-154
lines changed

9 files changed

+324
-154
lines changed

AssociatedObject.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Pod::Spec.new do |s|
2121

2222
s.prepare_command = 'swift build -c release && cp -f .build/release/AssociatedObjectPlugin ./Binary'
2323

24-
s.source_files = "Sources/AssociatedObject/AssociatedObject.swift"
24+
s.source_files = "Sources/AssociatedObject/AssociatedObject.swift", 'Sources/AssociatedObjectC/**/*.{c,h,m,swift}'
2525
s.swift_versions = "5.9"
2626

2727
s.preserve_paths = ["Binary/AssociatedObjectPlugin"]

Package.swift

+4
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,13 @@ let package = Package(
3030
.target(
3131
name: "AssociatedObject",
3232
dependencies: [
33+
"AssociatedObjectC",
3334
"AssociatedObjectPlugin"
3435
]
3536
),
37+
.target(
38+
name: "AssociatedObjectC"
39+
),
3640
.macro(
3741
name: "AssociatedObjectPlugin",
3842
dependencies: [

Sources/AssociatedObject/AssociatedObject.swift

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
@_exported import ObjectiveC
22

3+
#if canImport(AssociatedObjectC)
4+
@_exported import AssociatedObjectC
5+
#endif
6+
37
@attached(peer, names: arbitrary)
48
@attached(accessor)
59
public macro AssociatedObject(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
// associated_object_key.h
3+
//
4+
//
5+
// Created by p-x9 on 2024/01/26.
6+
//
7+
//
8+
9+
#ifndef associated_object_key_h
10+
#define associated_object_key_h
11+
12+
#if defined(__wasm__)
13+
// Wasm can't access call frame for security purposes
14+
#define get_return_address() ((void*) 0)
15+
#elif __GNUC__
16+
#define get_return_address() __builtin_return_address(0)
17+
#elif _MSC_VER
18+
#include <intrin.h>
19+
#define get_return_address() _ReturnAddress()
20+
#else
21+
#error missing implementation for get_return_address
22+
#define get_return_address() ((void*) 0)
23+
#endif
24+
25+
#ifdef __GNUC__
26+
#define NOINLINE __attribute__((noinline))
27+
#elif _MSC_VER
28+
#define NOINLINE __declspec(noinline)
29+
#else
30+
#error missing implementation for `NOINLINE`
31+
#define NOINLINE
32+
#endif
33+
34+
NOINLINE
35+
const void *_associated_object_key() {
36+
return get_return_address();
37+
}
38+
39+
#endif /* associated_object_key_h */

Sources/AssociatedObjectPlugin/AssociatedObjectMacro.swift

+28-7
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,22 @@ extension AssociatedObjectMacro: PeerMacro {
4949
return []
5050
}
5151

52+
let keyAccessor = """
53+
_associated_object_key()
54+
"""
55+
5256
let keyDecl = VariableDeclSyntax(
57+
attributes: [
58+
.attribute("@inline(never)")
59+
],
5360
bindingSpecifier: .identifier("static var"),
5461
bindings: PatternBindingListSyntax {
5562
PatternBindingSyntax(
5663
pattern: IdentifierPatternSyntax(identifier: .identifier("__associated_\(identifier.trimmed)Key")),
57-
typeAnnotation: .init(type: IdentifierTypeSyntax(name: .identifier("UInt8"))),
58-
initializer: InitializerClauseSyntax(value: ExprSyntax(stringLiteral: "0"))
64+
typeAnnotation: .init(type: IdentifierTypeSyntax(name: .identifier("UnsafeRawPointer"))),
65+
accessorBlock: .init(
66+
accessors: .getter("\(raw: keyAccessor)")
67+
)
5968
)
6069
}
6170
)
@@ -65,6 +74,7 @@ extension AssociatedObjectMacro: PeerMacro {
6574
]
6675

6776
if type.isOptional && defaultValue != nil {
77+
let flagName = "__associated_\(identifier.trimmed)IsSet"
6878
let flagDecl = VariableDeclSyntax(
6979
attributes: [
7080
.attribute("@_AssociatedObject(.OBJC_ASSOCIATION_ASSIGN)")
@@ -73,22 +83,33 @@ extension AssociatedObjectMacro: PeerMacro {
7383
bindings: PatternBindingListSyntax {
7484
PatternBindingSyntax(
7585
pattern: IdentifierPatternSyntax(
76-
identifier: .identifier("__associated_\(identifier.trimmed)IsSet")
86+
identifier: .identifier(flagName)
7787
),
7888
typeAnnotation: .init(type: IdentifierTypeSyntax(name: .identifier("Bool"))),
7989
initializer: InitializerClauseSyntax(value: BooleanLiteralExprSyntax(false))
8090
)
8191
}
8292
)
93+
94+
// nested peer macro will not expand
95+
// https://github.com/apple/swift/issues/69073
96+
let keyAccessor = """
97+
_associated_object_key()
98+
"""
8399
let flagKeyDecl = VariableDeclSyntax(
100+
attributes: [
101+
.attribute("@inline(never)")
102+
],
84103
bindingSpecifier: .identifier("static var"),
85104
bindings: PatternBindingListSyntax {
86105
PatternBindingSyntax(
87106
pattern: IdentifierPatternSyntax(
88107
identifier: .identifier("__associated___associated_\(identifier.trimmed)IsSetKey")
89108
),
90-
typeAnnotation: .init(type: IdentifierTypeSyntax(name: .identifier("UInt8"))),
91-
initializer: InitializerClauseSyntax(value: ExprSyntax(stringLiteral: "0"))
109+
typeAnnotation: .init(type: IdentifierTypeSyntax(name: .identifier("UnsafeRawPointer"))),
110+
accessorBlock: .init(
111+
accessors: .getter("\(raw: keyAccessor)")
112+
)
92113
)
93114
}
94115
)
@@ -160,7 +181,7 @@ extension AssociatedObjectMacro: AccessorMacro {
160181
return []
161182
}
162183

163-
var associatedKey: ExprSyntax = "&Self.__associated_\(identifier.trimmed)Key"
184+
var associatedKey: ExprSyntax = "Self.__associated_\(identifier.trimmed)Key"
164185
if case let .argumentList(arguments) = node.arguments,
165186
let element = arguments.first(where: { $0.label?.text == "key" }),
166187
let customKey = element.expression.as(ExprSyntax.self) {
@@ -236,7 +257,7 @@ extension AssociatedObjectMacro {
236257
"""
237258
if let value = objc_getAssociatedObject(
238259
self,
239-
&Self.__associated_\(identifier.trimmed)Key
260+
\(associatedKey)
240261
) as? \(varTypeWithoutOptional.trimmed) {
241262
return value
242263
} else {

0 commit comments

Comments
 (0)