ObjFW  Check-in [2d8c6a004d]

Overview
Comment:Backport associated objects to old Apple runtime
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 2d8c6a004df7bdb7860e515000581ab5191cf80ee16c19a6613117ec12bdbb19
User & Date: js on 2024-02-25 01:08:24
Other Links: manifest | tags
Context
2024-02-25
21:19
OFLHAArchiveEntry: Create more compatible archives check-in: 7752f8518b user: js tags: trunk
01:08
Backport associated objects to old Apple runtime check-in: 2d8c6a004d user: js tags: trunk
2024-02-24
22:30
Use `bti c` instead of `bti jc` check-in: ddb7ebcf5b user: js tags: trunk
Changes

Modified configure.ac from [43a1e3f56e] to [040a8ef356].

657
658
659
660
661
662
663
664


665
666
667
668
669
670
671

	AC_CHECK_FUNC(objc_autoreleasePoolPush, [], [
		AC_SUBST(RUNTIME_AUTORELEASE_M, "runtime/autorelease.m")
	])
	AC_CHECK_FUNC(objc_constructInstance, [], [
		AC_SUBST(RUNTIME_INSTANCE_M, "runtime/instance.m")
	])
	AC_CHECK_FUNCS(objc_setAssociatedObject)



	OBJCFLAGS="$old_OBJCFLAGS"
	;;
esac

case "$host_os" in
mint*)







|
>
>







657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673

	AC_CHECK_FUNC(objc_autoreleasePoolPush, [], [
		AC_SUBST(RUNTIME_AUTORELEASE_M, "runtime/autorelease.m")
	])
	AC_CHECK_FUNC(objc_constructInstance, [], [
		AC_SUBST(RUNTIME_INSTANCE_M, "runtime/instance.m")
	])
	AC_CHECK_FUNCS(objc_setAssociatedObject, [], [
		AC_SUBST(RUNTIME_ASSOCIATION_M, "runtime/association.m")
	])

	OBJCFLAGS="$old_OBJCFLAGS"
	;;
esac

case "$host_os" in
mint*)

Modified extra.mk.in from [5cc5b370f7] to [3b4be65212].

63
64
65
66
67
68
69

70
71
72
73
74
75
76
OF_POLL_KERNEL_EVENT_OBSERVER_M = @OF_POLL_KERNEL_EVENT_OBSERVER_M@
OF_SECURE_TRANSPORT_TLS_STREAM_M = @OF_SECURE_TRANSPORT_TLS_STREAM_M@
OF_SELECT_KERNEL_EVENT_OBSERVER_M = @OF_SELECT_KERNEL_EVENT_OBSERVER_M@
REEXPORT_RUNTIME = @REEXPORT_RUNTIME@
REEXPORT_RUNTIME_FRAMEWORK = @REEXPORT_RUNTIME_FRAMEWORK@
RUNTIME = @RUNTIME@
RUNTIME_ARC_TESTS_M = @RUNTIME_ARC_TESTS_M@

RUNTIME_AUTORELEASE_M = @RUNTIME_AUTORELEASE_M@
RUNTIME_FRAMEWORK_LIBS = @RUNTIME_FRAMEWORK_LIBS@
RUNTIME_INSTANCE_M = @RUNTIME_INSTANCE_M@
RUNTIME_LIBS = @RUNTIME_LIBS@
SUBPROCESS = @SUBPROCESS@
TESTPLUGIN = @TESTPLUGIN@
TESTPLUGIN_LIBS = @TESTPLUGIN_LIBS@







>







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
OF_POLL_KERNEL_EVENT_OBSERVER_M = @OF_POLL_KERNEL_EVENT_OBSERVER_M@
OF_SECURE_TRANSPORT_TLS_STREAM_M = @OF_SECURE_TRANSPORT_TLS_STREAM_M@
OF_SELECT_KERNEL_EVENT_OBSERVER_M = @OF_SELECT_KERNEL_EVENT_OBSERVER_M@
REEXPORT_RUNTIME = @REEXPORT_RUNTIME@
REEXPORT_RUNTIME_FRAMEWORK = @REEXPORT_RUNTIME_FRAMEWORK@
RUNTIME = @RUNTIME@
RUNTIME_ARC_TESTS_M = @RUNTIME_ARC_TESTS_M@
RUNTIME_ASSOCIATION_M = @RUNTIME_ASSOCIATION_M@
RUNTIME_AUTORELEASE_M = @RUNTIME_AUTORELEASE_M@
RUNTIME_FRAMEWORK_LIBS = @RUNTIME_FRAMEWORK_LIBS@
RUNTIME_INSTANCE_M = @RUNTIME_INSTANCE_M@
RUNTIME_LIBS = @RUNTIME_LIBS@
SUBPROCESS = @SUBPROCESS@
TESTPLUGIN = @TESTPLUGIN@
TESTPLUGIN_LIBS = @TESTPLUGIN_LIBS@

Modified src/Makefile from [cca3d2063b] to [914eea7d94].

218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
	OFRangeCharacterSet.m		\
	OFSandbox.m			\
	OFStrFTime.m			\
	OFStrPTime.m			\
	OFSubarray.m			\
	OFSubdata.m			\
	OFUTF8String.m			\
	${LIBBASES_M}			\
	${RUNTIME_AUTORELEASE_M}	\
	${RUNTIME_INSTANCE_M}		\
	${UNICODE_M}			\
	${USE_SRCS_TAGGED_POINTERS}
SRCS_FILES += OFFileIRIHandler.m
SRCS_SOCKETS += OFAsyncIPSocketConnector.m		\
		OFDNSResolverSettings.m			\







|







218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
	OFRangeCharacterSet.m		\
	OFSandbox.m			\
	OFStrFTime.m			\
	OFStrPTime.m			\
	OFSubarray.m			\
	OFSubdata.m			\
	OFUTF8String.m			\
	${RUNTIME_ASSOCIATION_M}	\
	${RUNTIME_AUTORELEASE_M}	\
	${RUNTIME_INSTANCE_M}		\
	${UNICODE_M}			\
	${USE_SRCS_TAGGED_POINTERS}
SRCS_FILES += OFFileIRIHandler.m
SRCS_SOCKETS += OFAsyncIPSocketConnector.m		\
		OFDNSResolverSettings.m			\

Modified src/OFObject.h from [cd5ab83dfc] to [18f66eccc2].

1459
1460
1461
1462
1463
1464
1465













1466
1467
1468
1469
1470
1471
1472
#ifdef OF_APPLE_RUNTIME
extern void *_Null_unspecified objc_autoreleasePoolPush(void);
extern void objc_autoreleasePoolPop(void *_Null_unspecified pool);
# ifndef __OBJC2__
extern id _Nullable objc_constructInstance(Class _Nullable class_,
    void *_Nullable bytes);
extern void *_Nullable objc_destructInstance(id _Nullable object);













# endif
#endif
extern id OFAllocObject(Class class_, size_t extraSize, size_t extraAlignment,
    void *_Nullable *_Nullable extra);
extern void OF_NO_RETURN_FUNC OFMethodNotFound(id self, SEL _cmd);

/**







>
>
>
>
>
>
>
>
>
>
>
>
>







1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
#ifdef OF_APPLE_RUNTIME
extern void *_Null_unspecified objc_autoreleasePoolPush(void);
extern void objc_autoreleasePoolPop(void *_Null_unspecified pool);
# ifndef __OBJC2__
extern id _Nullable objc_constructInstance(Class _Nullable class_,
    void *_Nullable bytes);
extern void *_Nullable objc_destructInstance(id _Nullable object);
typedef enum objc_associationPolicy {
	OBJC_ASSOCIATION_ASSIGN	= 0,
	OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
	OBJC_ASSOCIATION_RETAIN = OBJC_ASSOCIATION_RETAIN_NONATOMIC | 0x300,
	OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
	OBJC_ASSOCIATION_COPY = OBJC_ASSOCIATION_COPY_NONATOMIC | 0x300
} objc_associationPolicy;
extern void objc_setAssociatedObject(id _Nonnull object,
    const void *_Nonnull key, id _Nullable value,
    objc_associationPolicy policy);
extern id _Nullable objc_getAssociatedObject(id _Nonnull object,
    const void *_Nonnull key);
extern void objc_removeAssociatedObjects(id _Nonnull object);
# endif
#endif
extern id OFAllocObject(Class class_, size_t extraSize, size_t extraAlignment,
    void *_Nullable *_Nullable extra);
extern void OF_NO_RETURN_FUNC OFMethodNotFound(id self, SEL _cmd);

/**

Modified src/runtime/association.m from [b59c5c9746] to [0b42a1bcad].

11
12
13
14
15
16
17

18
19




20
21
22
23
24










































25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"


#import "ObjFWRT.h"
#import "private.h"





struct Association {
	id object;
	objc_associationPolicy policy;
};











































#ifdef OF_HAVE_THREADS
# define numSlots 8	/* needs to be a power of 2 */
# import "OFPlainMutex.h"
static OFSpinlock spinlocks[numSlots];
#else
# define numSlots 1
#endif
static struct objc_hashtable *hashtables[numSlots];

static OF_INLINE size_t
slotForObject(id object)
{
	return ((size_t)((uintptr_t)object >> 4) & (numSlots - 1));
}








>
|
|
>
>
>
>





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#ifdef OF_OBJFW_RUNTIME
# import "ObjFWRT.h"
# import "private.h"
#else
# import "OFObject.h"
# import "OFMapTable.h"
#endif

struct Association {
	id object;
	objc_associationPolicy policy;
};

#ifdef OF_OBJFW_RUNTIME
typedef struct objc_hashtable objc_hashtable;
#else
typedef OFMapTable objc_hashtable;
static const OFMapTableFunctions defaultFunctions = { NULL };

static objc_hashtable *
objc_hashtable_new(uint32_t (*hash)(const void *key),
    bool (*equal)(const void *key1, const void *key2), uint32_t size)
{
	return [[OFMapTable alloc] initWithKeyFunctions: defaultFunctions
					objectFunctions: defaultFunctions];
}

static void
objc_hashtable_set(objc_hashtable *hashtable, const void *key,
    const void *object)
{
	return [hashtable setObject: (void *)object forKey: (void *)key];
}

static void *
objc_hashtable_get(objc_hashtable *hashtable, const void *key)
{
	return [hashtable objectForKey: (void *)key];
}

static void
objc_hashtable_delete(objc_hashtable *hashtable, const void *key)
{
	[hashtable removeObjectForKey: (void *)key];
}

static void
objc_hashtable_free(objc_hashtable *hashtable)
{
	[hashtable release];
}

# define OBJC_ERROR(...) abort()
#endif

#ifdef OF_HAVE_THREADS
# define numSlots 8	/* needs to be a power of 2 */
# import "OFPlainMutex.h"
static OFSpinlock spinlocks[numSlots];
#else
# define numSlots 1
#endif
static objc_hashtable *hashtables[numSlots];

static OF_INLINE size_t
slotForObject(id object)
{
	return ((size_t)((uintptr_t)object >> 4) & (numSlots - 1));
}

87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

#ifdef OF_HAVE_THREADS
	if (OFSpinlockLock(&spinlocks[slot]) != 0)
		OBJC_ERROR("Failed to lock spinlock!");

	@try {
#endif
		struct objc_hashtable *objectHashtable;
		struct Association *association;

		objectHashtable = objc_hashtable_get(hashtables[slot], object);
		if (objectHashtable == NULL) {
			objectHashtable = objc_hashtable_new(hash, equal, 2);
			objc_hashtable_set(hashtables[slot], object,
			    objectHashtable);







|







134
135
136
137
138
139
140
141
142
143
144
145
146
147
148

#ifdef OF_HAVE_THREADS
	if (OFSpinlockLock(&spinlocks[slot]) != 0)
		OBJC_ERROR("Failed to lock spinlock!");

	@try {
#endif
		objc_hashtable *objectHashtable;
		struct Association *association;

		objectHashtable = objc_hashtable_get(hashtables[slot], object);
		if (objectHashtable == NULL) {
			objectHashtable = objc_hashtable_new(hash, equal, 2);
			objc_hashtable_set(hashtables[slot], object,
			    objectHashtable);
131
132
133
134
135
136
137

138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159

160
161

162
163
164
165
166
167
168


169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187

188
189
190
191
192
193
194
195
196







197
198
199
200
201
202
203
#endif
}

id
objc_getAssociatedObject(id object, const void *key)
{
	size_t slot = slotForObject(object);


#ifdef OF_HAVE_THREADS
	if (OFSpinlockLock(&spinlocks[slot]) != 0)
		OBJC_ERROR("Failed to lock spinlock!");

	@try {
#endif
		struct objc_hashtable *objectHashtable;
		struct Association *association;

		objectHashtable = objc_hashtable_get(hashtables[slot], object);
		if (objectHashtable == NULL)
			return nil;

		association = objc_hashtable_get(objectHashtable, key);
		if (association == NULL)
			return nil;

		switch (association->policy) {
		case OBJC_ASSOCIATION_RETAIN:
		case OBJC_ASSOCIATION_COPY:
			return [[association->object retain] autorelease];

		default:
			return association->object;

		}
#ifdef OF_HAVE_THREADS
	} @finally {
		if (OFSpinlockUnlock(&spinlocks[slot]) != 0)
			OBJC_ERROR("Failed to unlock spinlock!");
	}
#endif


}

void
objc_removeAssociatedObjects(id object)
{
	size_t slot = slotForObject(object);

#ifdef OF_HAVE_THREADS
	if (OFSpinlockLock(&spinlocks[slot]) != 0)
		OBJC_ERROR("Failed to lock spinlock!");

	@try {
#endif
		struct objc_hashtable *objectHashtable;

		objectHashtable = objc_hashtable_get(hashtables[slot], object);
		if (objectHashtable == NULL)
			return;


		for (uint32_t i = 0; i < objectHashtable->size; i++) {
			struct Association *association;

			if (objectHashtable->data[i] == NULL ||
			    objectHashtable->data[i] == &objc_deletedBucket)
				continue;

			association = (struct Association *)
			    objectHashtable->data[i]->object;








			switch (association->policy) {
			case OBJC_ASSOCIATION_RETAIN:
			case OBJC_ASSOCIATION_RETAIN_NONATOMIC:
			case OBJC_ASSOCIATION_COPY:
			case OBJC_ASSOCIATION_COPY_NONATOMIC:
				[association->object release];







>







|













|
>

|
>







>
>













|





>









>
>
>
>
>
>
>







178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
#endif
}

id
objc_getAssociatedObject(id object, const void *key)
{
	size_t slot = slotForObject(object);
	id ret;

#ifdef OF_HAVE_THREADS
	if (OFSpinlockLock(&spinlocks[slot]) != 0)
		OBJC_ERROR("Failed to lock spinlock!");

	@try {
#endif
		objc_hashtable *objectHashtable;
		struct Association *association;

		objectHashtable = objc_hashtable_get(hashtables[slot], object);
		if (objectHashtable == NULL)
			return nil;

		association = objc_hashtable_get(objectHashtable, key);
		if (association == NULL)
			return nil;

		switch (association->policy) {
		case OBJC_ASSOCIATION_RETAIN:
		case OBJC_ASSOCIATION_COPY:
			ret = [[association->object retain] autorelease];
			break;
		default:
			ret = association->object;
			break;
		}
#ifdef OF_HAVE_THREADS
	} @finally {
		if (OFSpinlockUnlock(&spinlocks[slot]) != 0)
			OBJC_ERROR("Failed to unlock spinlock!");
	}
#endif

	return ret;
}

void
objc_removeAssociatedObjects(id object)
{
	size_t slot = slotForObject(object);

#ifdef OF_HAVE_THREADS
	if (OFSpinlockLock(&spinlocks[slot]) != 0)
		OBJC_ERROR("Failed to lock spinlock!");

	@try {
#endif
		objc_hashtable *objectHashtable;

		objectHashtable = objc_hashtable_get(hashtables[slot], object);
		if (objectHashtable == NULL)
			return;

#ifdef OF_OBJFW_RUNTIME
		for (uint32_t i = 0; i < objectHashtable->size; i++) {
			struct Association *association;

			if (objectHashtable->data[i] == NULL ||
			    objectHashtable->data[i] == &objc_deletedBucket)
				continue;

			association = (struct Association *)
			    objectHashtable->data[i]->object;
#else
		OFMapTableEnumerator *enumerator =
		    [objectHashtable objectEnumerator];
		void **iter;
		while ((iter = [enumerator nextObject]) != NULL) {
			struct Association *association = *iter;
#endif

			switch (association->policy) {
			case OBJC_ASSOCIATION_RETAIN:
			case OBJC_ASSOCIATION_RETAIN_NONATOMIC:
			case OBJC_ASSOCIATION_COPY:
			case OBJC_ASSOCIATION_COPY_NONATOMIC:
				[association->object release];

Modified src/runtime/instance.m from [bbab5691ed] to [b70e964ac7].

19
20
21
22
23
24
25




26
27
28
29
30
31
32

#ifdef OF_OBJFW_RUNTIME
# import "ObjFWRT.h"
# import "private.h"
#else
# import <objc/runtime.h>
#endif





static SEL constructSelector = NULL;
static SEL destructSelector = NULL;

static bool
callConstructors(Class class, id object)
{







>
>
>
>







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

#ifdef OF_OBJFW_RUNTIME
# import "ObjFWRT.h"
# import "private.h"
#else
# import <objc/runtime.h>
#endif

#ifndef OF_OBJFW_RUNTIME
extern void objc_removeAssociatedObjects(id object);
#endif

static SEL constructSelector = NULL;
static SEL destructSelector = NULL;

static bool
callConstructors(Class class, id object)
{
98
99
100
101
102
103
104
105
106
107
108
109
110
				destruct(object, destructSelector);

			last = destruct;
		} else
			break;
	}

#if defined(OF_OBJFW_RUNTIME) || defined(HAVE_OBJC_SETASSOCIATEDOBJECT)
	objc_removeAssociatedObjects(object);
#endif

	return object;
}







<

<



102
103
104
105
106
107
108

109

110
111
112
				destruct(object, destructSelector);

			last = destruct;
		} else
			break;
	}


	objc_removeAssociatedObjects(object);


	return object;
}

Modified tests/RuntimeTests.m from [358d865eaa] to [6e0282335c].

82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
	OFMutableString *string = [OFMutableString stringWithString: @"foo"];

	_test.bar = string;
	OTAssertEqual(_test.bar, string);
	OTAssertEqual(string.retainCount, 3);
}

#if defined(OF_OBJFW_RUNTIME) || defined(HAVE_OBJC_SETASSOCIATEDOBJECT)
- (void)testAssociatedObjects
{
	objc_setAssociatedObject(self, testKey, _test, OBJC_ASSOCIATION_ASSIGN);
	OTAssertEqual(_test.retainCount, 1);

	objc_setAssociatedObject(self, testKey, _test, OBJC_ASSOCIATION_RETAIN);
	OTAssertEqual(_test.retainCount, 2);







<







82
83
84
85
86
87
88

89
90
91
92
93
94
95
	OFMutableString *string = [OFMutableString stringWithString: @"foo"];

	_test.bar = string;
	OTAssertEqual(_test.bar, string);
	OTAssertEqual(string.retainCount, 3);
}


- (void)testAssociatedObjects
{
	objc_setAssociatedObject(self, testKey, _test, OBJC_ASSOCIATION_ASSIGN);
	OTAssertEqual(_test.retainCount, 1);

	objc_setAssociatedObject(self, testKey, _test, OBJC_ASSOCIATION_RETAIN);
	OTAssertEqual(_test.retainCount, 2);
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121

	OTAssertEqual(objc_getAssociatedObject(self, testKey), _test);
	OTAssertEqual(_test.retainCount, 3);

	objc_removeAssociatedObjects(self);
	OTAssertEqual(_test.retainCount, 2);
}
#endif

#ifdef OF_OBJFW_RUNTIME
- (void)testTaggedPointers
{
	int classID;
	uintmax_t value;
	id object;







<







106
107
108
109
110
111
112

113
114
115
116
117
118
119

	OTAssertEqual(objc_getAssociatedObject(self, testKey), _test);
	OTAssertEqual(_test.retainCount, 3);

	objc_removeAssociatedObjects(self);
	OTAssertEqual(_test.retainCount, 2);
}


#ifdef OF_OBJFW_RUNTIME
- (void)testTaggedPointers
{
	int classID;
	uintmax_t value;
	id object;