Objectively
Object oriented framework for C.
Loading...
Searching...
No Matches
Class.c File Reference
#include "Config.h"
#include <assert.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <link.h>
#include "Class.h"
#include "Object.h"

Go to the source code of this file.

Macros

#define MAX_CLASS_IMAGES   8
 The registered images providing Classes.
 

Functions

ident _alloc (Class *clazz)
 Instantiate a type through the given Class.
 
ident _cast (const Class *clazz, const ident obj)
 Perform a type-checking cast.
 
Class_initialize (const ClassDef *def)
 Initializes the given Class.
 
void addClassImage (ident handle)
 Registers an image that provides Classes, e.g. a plugin.
 
ClassclassForName (const char *name)
 
static const ident imageForAddress (const ident address)
 
static ident imageForHandle (ident handle)
 
ident release (ident obj)
 Atomically decrement the given Object's reference count. If the resulting reference count is 0, the Object is deallocated.
 
void removeClassImage (ident handle)
 Unregisters an image, and every Class it declared.
 
ident retain (ident obj)
 Atomically increment the given Object's reference count.
 
static void setup (void)
 Called when initializing Object to setup Objectively.
 
static void teardown (void)
 Called atexit to teardown Objectively.
 

Variables

static Class_classes
 
static size_t _classImageCount
 
static ident _classImages [MAX_CLASS_IMAGES]
 
size_t _pageSize
 

Macro Definition Documentation

◆ MAX_CLASS_IMAGES

#define MAX_CLASS_IMAGES   8

The registered images providing Classes.

Remarks
This is a plain array rather than a MutableArray because Class is beneath the collections: initializing one here would reenter _initialize. Images are added and removed as they are loaded and closed, not per lookup.

Definition at line 57 of file Class.c.

Function Documentation

◆ _alloc()

ident _alloc ( Class clazz)

Instantiate a type through the given Class.

Definition at line 170 of file Class.c.

170 {
171
172 ident obj = calloc(1, clazz->def.instanceSize);
173 assert(obj);
174
175 Object *object = (Object *) obj;
176
177 object->magic = OBJECTIVELY_MAGIC;
178 object->clazz = clazz;
179 object->referenceCount = 1;
180
181 ident interface = clazz->interface;
182 do {
183 *(ident *) (obj + clazz->def.interfaceOffset) = interface;
184 } while ((clazz = clazz->def.superclass));
185
186 return obj;
187}
#define obj
#define OBJECTIVELY_MAGIC
The header value identifying Objectively types.
Definition Class.h:205
void * ident
The identity type, similar to Objective-C id.
Definition Types.h:49
size_t instanceSize
The instance size (required).
Definition Class.h:69
ptrdiff_t interfaceOffset
The interface offset (required).
Definition Class.h:74
Class * superclass
The superclass (required). e.g. _Object().
Definition Class.h:89
ClassDef def
The Class definition.
Definition Class.h:100
Object is the root Class of The Objectively Class hierarchy.
Definition Object.h:46
unsigned int magic
A header to allow introspection of Object types.
Definition Object.h:50

◆ _cast()

ident _cast ( const Class clazz,
const ident  obj 
)

Perform a type-checking cast.

Definition at line 189 of file Class.c.

189 {
190
191 if (obj) {
192 const Class *c = ((Object *) obj)->clazz;
193 while (c) {
194
195 // as a special case, we optimize for _Object
196 if (c == clazz || clazz == _Object()) {
197 break;
198 }
199
200 c = c->def.superclass;
201 }
202 assert(c);
203 }
204
205 return (ident) obj;
206}
Class * _Object(void)
Definition Object.c:136
The runtime representation of a Class.
Definition Class.h:95

◆ _initialize()

Class * _initialize ( const ClassDef clazz)

Initializes the given Class.

Parameters
clazzThe Class descriptor.
Returns
The initialized Class.

Definition at line 129 of file Class.c.

129 {
130
131 static Once once;
132 do_once(&once, setup());
133
134 assert(def);
135 assert(def->name);
136 assert(def->instanceSize);
137 assert(def->interfaceSize);
138 assert(def->interfaceOffset);
139
140 Class *clazz = calloc(1, sizeof(Class));
141 assert(clazz);
142
143 clazz->def = *def;
144
145 clazz->interface = calloc(1, def->interfaceSize);
146 assert(clazz->interface);
147
148 Class *superclass = clazz->def.superclass;
149 if (superclass) {
150
151 assert(superclass->def.instanceSize <= def->instanceSize);
152 assert(superclass->def.interfaceSize <= def->interfaceSize);
153
154 memcpy(clazz->interface, superclass->interface, superclass->def.interfaceSize);
155 }
156
157 if (clazz->def.initialize) {
158 clazz->def.initialize(clazz);
159 }
160
161 /* def.name is a literal in the declaring image, where the ClassDef itself is
162 * a compound literal with automatic storage. */
163 clazz->image = imageForAddress((ident) def->name);
164
165 clazz->next = __sync_lock_test_and_set(&_classes, clazz);
166
167 return clazz;
168}
static const ident imageForAddress(const ident address)
Definition Class.c:109
static void setup(void)
Called when initializing Object to setup Objectively.
Definition Class.c:91
static Class * _classes
Definition Class.c:49
long Once
The Once type.
Definition Once.h:37
#define do_once(once, block)
Executes the given block at most one time.
Definition Once.h:43
size_t interfaceSize
The interface size (required).
Definition Class.h:79
const char * name
The Class name (required).
Definition Class.h:84
void(* initialize)(Class *clazz)
The Class initializer (optional).
Definition Class.h:64
ident image
The base address of the image that declared this Class.
Definition Class.h:118
Class * next
Provides chaining of initialized Classes.
Definition Class.h:110
ident interface
The interface of the Class.
Definition Class.h:105

◆ addClassImage()

void addClassImage ( ident  handle)

Registers an image that provides Classes, e.g. a plugin.

Parameters
handleA handle from dlopen.

classForName resolves a name it has not yet initialized through the process-wide namespace, which holds only images loaded RTLD_GLOBAL, and which Windows does not have at all. An application that loads Classes from a plugin registers it here instead, and may then load it RTLD_LOCAL - which is how it keeps two plugins exporting the same symbols from coalescing.

Remarks
Registered images are searched most recently added first, so a newly loaded plugin answers ahead of the one it replaced.
Resolution is by Objectively's own convention, the Class name prefixed with an underscore, so nothing about the image has to be declared.

Definition at line 284 of file Class.c.

284 {
285
286 assert(handle);
288
289 _classImages[_classImageCount++] = handle;
290}
#define MAX_CLASS_IMAGES
The registered images providing Classes.
Definition Class.c:57
static ident _classImages[MAX_CLASS_IMAGES]
Definition Class.c:58
static size_t _classImageCount
Definition Class.c:59

◆ classForName()

Class * classForName ( const char *  name)
Returns
The Class with the given name, or NULL if no such Class has been initialized.
Remarks
Classes already initialized are answered first, then each registered image, then the process-wide namespace.

Definition at line 292 of file Class.c.

292 {
293
294 if (name) {
295 Class *c = _classes;
296 while (c) {
297 if (strcmp(name, c->def.name) == 0) {
298 return c;
299 }
300 c = c->next;
301 }
302
303 char *s;
304 if (asprintf(&s, "_%s", name) > 0) {
305 Class *clazz = NULL;
306 Class *(*archetype)(void) = NULL;
307
308 for (size_t i = _classImageCount; i > 0 && archetype == NULL; i--) {
309 archetype = dlsym(_classImages[i - 1], s);
310 }
311
312 if (archetype == NULL) {
313#if defined(_WIN32)
314 static Once once;
315 static ident handle;
316 do_once(&once, { handle = dlopen(NULL, RTLD_LAZY); });
317 archetype = handle ? dlsym(handle, s) : NULL;
318#else
319 archetype = dlsym(RTLD_DEFAULT, s);
320#endif
321 }
322
323 if (archetype) {
324 clazz = archetype();
325 }
326
327 free(s);
328 return clazz;
329 }
330 }
331
332 return NULL;
333}

◆ imageForAddress()

static const ident imageForAddress ( const ident  address)
static
Returns
The base address of the image containing address, or NULL.
Remarks
The Windows dlfcn shim has no dladdr. UNCHANGED_REFCOUNT matters: without it this would pin the very module the caller is about to release.

Definition at line 109 of file Class.c.

109 {
110
111 assert(address);
112
113#if defined(_WIN32)
114 HMODULE module;
115 if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
116 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR) address, &module)) {
117 return module;
118 }
119#else
120 Dl_info info;
121 if (dladdr(address, &info)) {
122 return info.dli_fbase;
123 }
124#endif
125
126 return NULL;
127}
static void info(const Log *self, const char *fmt,...)
Definition Log.c:118

◆ imageForHandle()

static ident imageForHandle ( ident  handle)
static
Returns
The base address of the image behind handle, or NULL.
Remarks
There is no one call for this. Windows hands out the module itself as the handle, which GetModuleFileName confirms rather than assumes; glibc answers from the link map; and macOS, which has neither, is left with matching the handle against the loaded images.

Definition at line 215 of file Class.c.

215 {
216
217 assert(handle);
218
219#if defined(_WIN32)
220 char path[MAX_PATH];
221 if (GetModuleFileNameA((HMODULE) handle, path, sizeof(path))) {
222 return handle;
223 }
224#elif defined(__APPLE__)
225 for (uint32_t i = 0; i < _dyld_image_count(); i++) {
226
227 /* RTLD_LOCAL is not the default on macOS, and dlopen of an image already
228 * loaded promotes it to the global namespace, which would put every image in
229 * the process there - undoing the isolation the caller loaded it for. */
230 ident image = dlopen(_dyld_get_image_name(i), RTLD_LAZY | RTLD_NOLOAD | RTLD_LOCAL);
231 if (image == NULL) {
232 continue;
233 }
234
235 dlclose(image);
236
237 if (image == handle) {
238 return (ident) _dyld_get_image_header(i);
239 }
240 }
241#else
242 struct link_map *map;
243 if (dlinfo(handle, RTLD_DI_LINKMAP, &map) == 0 && map) {
244 return (ident) map->l_addr;
245 }
246#endif
247
248 return NULL;
249}
static void map(Array *self, Functor functor, ident data)
Definition Array.c:536

◆ release()

ident release ( ident  obj)

Atomically decrement the given Object's reference count. If the resulting reference count is 0, the Object is deallocated.

Returns
This function always returns NULL.

Definition at line 335 of file Class.c.

335 {
336
337 if (obj) {
338 Object *object = cast(Object, obj);
339
340 assert(object);
341
342 if (__sync_add_and_fetch(&object->referenceCount, -1) == 0) {
343 $(object, dealloc);
344 }
345 }
346
347 return NULL;
348}
static void dealloc(Object *self)
Definition Array.c:99
#define cast(type, obj)
Safely cast obj to type.
Definition Class.h:228

◆ removeClassImage()

void removeClassImage ( ident  handle)

Unregisters an image, and every Class it declared.

Parameters
handleThe handle given to addClassImage.
Remarks
Classes initialized from an image outlive it otherwise: they are cached by name, and classForName answers from that cache ahead of any image. On a platform where closing an image really unmaps it - which dlclose does on Linux and FreeLibrary does on Windows - the next lookup would then read a ClassDef that is no longer mapped. classForName compares the name of every Class it walks, so one left behind breaks every lookup, not only its own.
The Classes are not destroyed, and this is not an oversight. dlclose does not unmap on macOS, so an archetype that has already run keeps answering from its own static Class * for as long as the process lives, and would hand back whatever this freed. Unregistering is the only thing that is safe whether the image goes away or stays: the Class becomes unreachable by name, and remains valid for the archetype that owns it. The cost is the Class and its interface, which are not reclaimed.
MUST be called while the handle is still open, and only once nothing instantiated from that image survives.

Definition at line 251 of file Class.c.

251 {
252
253 assert(handle);
254
255 for (size_t i = 0; i < _classImageCount; i++) {
256 if (_classImages[i] == handle) {
257
258 memmove(_classImages + i, _classImages + i + 1,
259 (_classImageCount - i - 1) * sizeof(ident));
260
262 break;
263 }
264 }
265
266 const ident image = imageForHandle(handle);
267 if (image == NULL) {
268 return;
269 }
270
271 Class **link = &_classes;
272 while (*link) {
273 Class *clazz = *link;
274
275 if (clazz->image == image) {
276 *link = clazz->next;
277 clazz->next = NULL;
278 } else {
279 link = &clazz->next;
280 }
281 }
282}
static ident imageForHandle(ident handle)
Definition Class.c:215

◆ retain()

ident retain ( ident  obj)

Atomically increment the given Object's reference count.

Returns
The Object.
Remarks
By calling this, the caller is expressing ownership of the Object, and preventing it from being released. Be sure to balance calls to retain with calls to release.

Definition at line 350 of file Class.c.

350 {
351
352 Object *object = cast(Object, obj);
353
354 assert(object);
355
356 __sync_add_and_fetch(&object->referenceCount, 1);
357
358 return obj;
359}

◆ setup()

static void setup ( void  )
static

Called when initializing Object to setup Objectively.

Definition at line 91 of file Class.c.

91 {
92
93 _classes = NULL;
94
95#if !defined(_SC_PAGESIZE)
96 _pageSize = 4096;
97#else
98 _pageSize = sysconf(_SC_PAGESIZE);
99#endif
100
101 atexit(teardown);
102}
static void teardown(void)
Called atexit to teardown Objectively.
Definition Class.c:64
size_t _pageSize
Definition Class.c:47

◆ teardown()

static void teardown ( void  )
static

Called atexit to teardown Objectively.

Definition at line 64 of file Class.c.

64 {
65 Class *c;
66
67 c = _classes;
68 while (c) {
69 if (c->def.destroy) {
70 c->def.destroy(c);
71 }
72
73 c = c->next;
74 }
75
76 c = _classes;
77 while (c) {
78
79 Class *next = c->next;
80
81 free(c->interface);
82 free(c);
83
84 c = next;
85 }
86}
static Unicode next(StringReader *self, StringReaderMode mode)
void(* destroy)(Class *clazz)
The Class destructor (optional). This method is run for initialized Classes when your application exi...
Definition Class.h:47

Variable Documentation

◆ _classes

Class* _classes
static

Definition at line 49 of file Class.c.

◆ _classImageCount

size_t _classImageCount
static

Definition at line 59 of file Class.c.

◆ _classImages

ident _classImages[MAX_CLASS_IMAGES]
static

Definition at line 58 of file Class.c.

◆ _pageSize

size_t _pageSize

Definition at line 47 of file Class.c.