1 /**
2 Objective-C runtime trickery for interfacing.
3 
4 Copyright: Guillaume Piolat 2016.
5 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)    
6 
7 Unknown licence from:
8 
9 Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
10 Non-NIB-Code & other changes: Max Horn <max@quendi.de>
11 Port to the D programming language: Jacob Carlborg <jacob.carlborg@gmail.com>
12 Resurrected by: Guillaume Piolat <contact@auburnsounds.com> for the purpose of audio plug-ins
13 
14 It just says "Feel free to customize this file for your purpose"
15 
16 TODO ask original authors of the runtime trickery for a licence
17 */
18 module derelict.cocoa.runtime;
19 
20 
21 /// Important reading: The "OS X ABI Function Call Guide"
22 /// https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/
23 
24 import core.stdc.config;
25 import core.atomic;
26 
27 import std.string;
28 
29 import dplug.core.nogc;
30 
31 
32 version(X86)
33     version = AnyX86;
34 version(X86_64)
35     version = AnyX86;
36 
37 //version = useTLS;
38 
39 // NSGeometry.h
40 
41 alias NSInteger = ptrdiff_t;
42 alias NSUInteger = size_t;
43 
44 static if ((void*).sizeof > int.sizeof) // 64bit
45     alias CGFloat = double;
46 else
47     alias CGFloat = float;
48 
49 struct NSPoint
50 {
51     CGFloat x;
52     CGFloat y;
53 }
54 
55 struct NSRange
56 {
57     NSUInteger location;
58     NSUInteger length;
59 }
60 
61 struct NSRect
62 {
63     NSPoint origin;
64     NSSize size;
65 }
66 
67 NSRect NSMakeRect(CGFloat x, CGFloat y, CGFloat w, CGFloat h) nothrow @nogc
68 {
69     return NSRect(NSPoint(x, y), NSSize(w, h));
70 }
71 
72 struct NSSize
73 {
74     CGFloat width;
75     CGFloat height;
76 }
77 
78 alias SEL = char*;
79 alias Class = objc_class*;
80 alias id = objc_object*;
81 
82 
83 alias BOOL = char;
84 enum : BOOL
85 {
86     NO = 0,
87     YES = 1
88 }
89 
90 alias Ivar = objc_ivar*;
91 alias Method = objc_method*;
92 alias Protocol = objc_object;
93 
94 
95 
96 alias IMP = extern (C) id function(id, SEL, ...);
97 
98 struct objc_object
99 {
100     Class isa;
101 }
102 
103 struct objc_super
104 {
105     id receiver;
106     Class clazz;
107 }
108 
109 struct objc_class
110 {
111     Class isa;
112     Class super_class;
113     const char* name;
114     c_long versionn;
115     c_long info;
116     c_long instance_size;
117     objc_ivar_list* ivars;
118     objc_method_list** methodLists;
119     objc_cache* cache;
120     objc_protocol_list* protocols;
121 }
122 
123 alias objc_property_t = void*;
124 
125 struct objc_ivar
126 {
127     char* ivar_name;
128     char* ivar_type;
129     int ivar_offset;
130 
131     version (X86_64)
132         int space;
133 }
134 
135 struct objc_ivar_list
136 {
137     int ivar_count;
138 
139     version (X86_64)
140         int space;
141 
142     /* variable length structure */
143     objc_ivar[1] ivar_list;
144 }
145 
146 struct objc_method
147 {
148     SEL method_name;
149     char* method_types;
150     IMP method_imp;
151 }
152 
153 struct objc_method_list
154 {
155     objc_method_list* obsolete;
156 
157     int method_count;
158 
159     version (X86_64)
160         int space;
161 
162     /* variable length structure */
163     objc_method[1] method_list;
164 }
165 
166 struct objc_cache
167 {
168     uint mask /* total = mask + 1 */;
169     uint occupied;
170     Method[1] buckets;
171 }
172 
173 struct objc_protocol_list
174 {
175     objc_protocol_list* next;
176     long count;
177     Protocol*[1] list;
178 }
179 
180 //Objective-C runtime bindings from the Cocoa framework
181 extern (C) nothrow @nogc
182 {
183     alias Class function (Class superclass) pfobjc_registerClassPair;
184 
185     alias bool function (Class cls, const(char)* name, size_t size, byte alignment, const(char)* types) pfclass_addIvar;
186     alias bool function (Class cls, const(SEL) name, IMP imp, const(char)* types) pfclass_addMethod;
187     alias Class function (Class superclass, const(char)* name, size_t extraBytes) pfobjc_allocateClassPair;
188     alias void function(Class cls) pfobjc_disposeClassPair;
189     alias id function (const(char)* name) pfobjc_getClass;
190     alias id function (const(char)* name) pfobjc_lookUpClass;
191 
192     alias id function (id theReceiver, SEL theSelector, ...) pfobjc_msgSend;
193     alias id function (objc_super* superr, SEL op, ...) pfobjc_msgSendSuper;
194 
195     version (AnyX86)
196         alias void function (void* stretAddr, id theReceiver, SEL theSelector, ...) pfobjc_msgSend_stret;
197 
198     alias const(char)* function (id obj) pfobject_getClassName;
199     alias Ivar function (id obj, const(char)* name, void** outValue) pfobject_getInstanceVariable;
200     alias Ivar function (id obj, const(char)* name, void* value) pfobject_setInstanceVariable;
201     alias SEL function (const(char)* str) pfsel_registerName;
202     version (X86)
203         alias double function (id self, SEL op, ...) pfobjc_msgSend_fpret;
204 
205     alias Method function (Class aClass, const(SEL) aSelector) pfclass_getInstanceMethod;
206     alias IMP function (Method method, IMP imp) pfmethod_setImplementation;
207 
208 
209     // like pfobjc_msgSend except for returning NSPoint
210     alias NSPoint function (id theReceiver, const(SEL) theSelector, ...) pfobjc_msgSend_NSPointret;
211 
212 
213     alias pfobjc_getProtocol = Protocol* function (const(char)* name);
214     alias pfclass_addProtocol = BOOL function (Class cls, Protocol* protocol);
215     alias pfobjc_allocateProtocol = Protocol* function(const(char)* name);
216     alias pfobjc_registerProtocol = void function(Protocol *proto);
217     alias pfclass_conformsToProtocol = BOOL function(Class cls, Protocol *protocol);
218 
219     alias pfprotocol_addMethodDescription = void function(Protocol *proto, SEL name, const char *types, BOOL isRequiredMethod, BOOL isInstanceMethod);
220 }
221 
222 __gshared
223 {
224     pfobjc_registerClassPair objc_registerClassPair;
225 
226     pfclass_addIvar varclass_addIvar;
227     pfclass_addMethod varclass_addMethod;
228     pfobjc_allocateClassPair varobjc_allocateClassPair;
229     pfobjc_disposeClassPair objc_disposeClassPair;
230     pfobjc_getClass varobjc_getClass;
231     pfobjc_lookUpClass varobjc_lookUpClass;
232 
233     pfobjc_msgSend objc_msgSend;
234     pfobjc_msgSendSuper objc_msgSendSuper;
235 
236     version(AnyX86)
237         pfobjc_msgSend_stret objc_msgSend_stret;
238 
239     version(X86)
240         pfobjc_msgSend_fpret objc_msgSend_fpret;
241 
242     pfobject_getClassName varobject_getClassName;
243     pfobject_getInstanceVariable object_getInstanceVariable;
244     pfobject_setInstanceVariable object_setInstanceVariable;
245     pfsel_registerName varsel_registerName;
246 
247     pfclass_getInstanceMethod varclass_getInstanceMethod;
248     pfmethod_setImplementation method_setImplementation;
249 
250     pfobjc_getProtocol objc_getProtocol;
251     pfclass_addProtocol class_addProtocol;
252     pfobjc_allocateProtocol objc_allocateProtocol;
253     pfobjc_registerProtocol objc_registerProtocol;
254     pfclass_conformsToProtocol class_conformsToProtocol;
255     pfprotocol_addMethodDescription protocol_addMethodDescription;
256 }
257 
258 bool class_addIvar (Class cls, string name, size_t size, byte alignment, string types) nothrow @nogc
259 {
260     return varclass_addIvar(cls, CString(name), size, alignment, CString(types));
261 }
262 
263 bool class_addMethod (Class cls, SEL name, IMP imp, string types) nothrow @nogc
264 {
265     return varclass_addMethod(cls, name, imp, CString(types));
266 }
267 
268 Class objc_allocateClassPair (Class superclass, const(char)* name, size_t extraBytes) nothrow @nogc
269 {
270     return varobjc_allocateClassPair(superclass, name, extraBytes);
271 }
272 
273 id objc_getClass (string name) nothrow @nogc
274 {
275     return varobjc_getClass(CString(name));
276 }
277 
278 id objc_getClass (char* name) nothrow @nogc
279 {
280     return varobjc_getClass(name);
281 }
282 
283 id objc_lookUpClass (string name) nothrow @nogc
284 {
285     return varobjc_lookUpClass(CString(name));
286 }
287 /*
288 string object_getClassName (id obj) nothrow @nogc
289 {
290     return fromStringz(varobject_getClassName(obj)).idup;
291 }
292 */
293 SEL sel_registerName (string str) nothrow @nogc
294 {
295     return varsel_registerName(CString(str));
296 }
297 
298 Method class_getInstanceMethod (Class aClass, string aSelector) nothrow @nogc
299 {
300     return varclass_getInstanceMethod(aClass, CString(aSelector));
301 }
302 
303 // Lazy selector literal
304 // eg: sel!"init"
305 SEL sel(string selectorName)() nothrow @nogc
306 {
307     version(useTLS)
308     {
309         // Use of TLS here
310         static size_t cached = 0;
311         if (cached == 0)
312         {
313             cached = cast(size_t)( sel_registerName(selectorName) );        
314         }
315         return cast(SEL) cached;
316     }
317     else
318     {
319         // we use type-punning here because deep shared(T) is annoying
320         shared(size_t) cached = 0;
321         size_t got = atomicLoad(cached);
322         if (got == 0)
323         {
324             got = cast(size_t)( sel_registerName(selectorName) );
325             atomicStore(cached, got);
326         }
327         return cast(SEL) got;
328     }
329 }
330 
331 // Lazy class object
332 // eg: lazyClass!"NSObject"
333 id lazyClass(string className)() nothrow @nogc
334 {
335     version(useTLS)
336     {
337         // Use of TLS here
338         static size_t cached = 0;
339         if (cached == 0)
340         {
341             cached = cast(size_t)( objc_getClass(className) );        
342         }
343         return cast(id) cached;
344     }
345     else
346     {
347         // we use type-punning here because deep shared(T) is annoying
348         shared(size_t) cached = 0;
349         size_t got = atomicLoad(cached);
350         if (got == 0)
351         {
352             got = cast(size_t)( objc_getClass(className) );
353             atomicStore(cached, got);
354         }
355         return cast(id) got;
356     }
357 }
358 
359 Protocol* lazyProtocol(string className)() nothrow @nogc
360 {
361     version(useTLS)
362     {
363         static size_t cached = 0;
364         if (cached == 0)
365         {
366             cached = cast(size_t)( objc_getProtocol(className) );        
367         }
368         return cast(Protocol*) cached;
369     }
370     else
371     {
372         // we use type-punning here because deep shared(T) is annoying
373         shared(size_t) cached = 0;
374         size_t got = atomicLoad(cached);
375         if (got == 0)
376         {
377             got = cast(size_t)( objc_getProtocol(className) );
378             atomicStore(cached, got);
379         }
380         return cast(Protocol*) got;
381     }
382 }
383 
384 // @encode replacement
385 template encode(T)
386 {
387     static if (is(T == int))
388         enum encode = "i";
389     else static if (is(T == NSRect))
390     {
391         enum encode = "{_NSRect={_NSPoint=dd}{_NSSize=dd}}";
392     }
393     else static if (is(T == NSSize))
394     {
395         enum encode = "{_NSSize=dd}";
396     }
397     else
398         static assert(false, "TODO implement encode for type " ~ T.stringof);
399 }