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     CString nameZ = CString(name);
261     CString typesZ = CString(types);
262     return varclass_addIvar(cls, nameZ.storage, size, alignment, typesZ.storage);
263 }
264 
265 bool class_addMethod (Class cls, SEL name, IMP imp, string types) nothrow @nogc
266 {
267     CString typesZ = CString(types); 
268     return varclass_addMethod(cls, name, imp, typesZ.storage);
269 }
270 
271 Class objc_allocateClassPair (Class superclass, const(char)* name, size_t extraBytes) nothrow @nogc
272 {
273     return varobjc_allocateClassPair(superclass, name, extraBytes);
274 }
275 
276 id objc_getClass (string name) nothrow @nogc
277 {
278     CString nameZ = CString(name);
279     return varobjc_getClass(nameZ.storage);
280 }
281 
282 id objc_getClass (char* name) nothrow @nogc
283 {
284     return varobjc_getClass(name);
285 }
286 
287 id objc_lookUpClass (string name) nothrow @nogc
288 {
289     CString nameZ = CString(name);
290     return varobjc_lookUpClass(nameZ.storage);
291 }
292 /*
293 string object_getClassName (id obj) nothrow @nogc
294 {
295     return fromStringz(varobject_getClassName(obj)).idup;
296 }
297 */
298 SEL sel_registerName (string str) nothrow @nogc
299 {
300     CString strZ = CString(str);
301     return varsel_registerName(strZ.storage);
302 }
303 
304 Method class_getInstanceMethod (Class aClass, string aSelector) nothrow @nogc
305 {
306     CString aSelectorZ = CString(aSelector);
307     return varclass_getInstanceMethod(aClass, aSelectorZ.storage);
308 }
309 
310 // Lazy selector literal
311 // eg: sel!"init"
312 SEL sel(string selectorName)() nothrow @nogc
313 {
314     // PERF: too much allocation here
315     version(useTLS)
316     {
317         // Use of TLS here
318         static size_t cached = 0;
319         if (cached == 0)
320         {
321             cached = cast(size_t)( sel_registerName(selectorName) );        
322         }
323         return cast(SEL) cached;
324     }
325     else
326     {
327         // we use type-punning here because deep shared(T) is annoying
328         shared(size_t) cached = 0;
329         size_t got = atomicLoad(cached);
330         if (got == 0)
331         {
332             got = cast(size_t)( sel_registerName(selectorName) );
333             atomicStore(cached, got);
334         }
335         return cast(SEL) got;
336     }
337 }
338 
339 // Lazy class object
340 // eg: lazyClass!"NSObject"
341 id lazyClass(string className)() nothrow @nogc
342 {
343     // PERF: too much allocation here
344     version(useTLS)
345     {
346         // Use of TLS here
347         static size_t cached = 0;
348         if (cached == 0)
349         {
350             cached = cast(size_t)( objc_getClass(className) );        
351         }
352         return cast(id) cached;
353     }
354     else
355     {
356         // we use type-punning here because deep shared(T) is annoying
357         shared(size_t) cached = 0;
358         size_t got = atomicLoad(cached);
359         if (got == 0)
360         {
361             got = cast(size_t)( objc_getClass(className) );
362             atomicStore(cached, got);
363         }
364         return cast(id) got;
365     }
366 }
367 
368 Protocol* lazyProtocol(string className)() nothrow @nogc
369 {
370     // PERF: too much allocation here
371     version(useTLS)
372     {
373         static size_t cached = 0;
374         if (cached == 0)
375         {
376             cached = cast(size_t)( objc_getProtocol(className) );        
377         }
378         return cast(Protocol*) cached;
379     }
380     else
381     {
382         // we use type-punning here because deep shared(T) is annoying
383         shared(size_t) cached = 0;
384         size_t got = atomicLoad(cached);
385         if (got == 0)
386         {
387             got = cast(size_t)( objc_getProtocol(className) );
388             atomicStore(cached, got);
389         }
390         return cast(Protocol*) got;
391     }
392 }
393 
394 // @encode replacement
395 template encode(T)
396 {
397     static if (is(T == int))
398         enum encode = "i";
399     else static if (is(T == NSRect))
400     {
401         enum encode = "{_NSRect={_NSPoint=dd}{_NSSize=dd}}";
402     }
403     else static if (is(T == NSSize))
404     {
405         enum encode = "{_NSSize=dd}";
406     }
407     else
408         static assert(false, "TODO implement encode for type " ~ T.stringof);
409 }