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 bindbc.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_getMetaClass;
191     alias id function (const(char)* name) pfobjc_lookUpClass;
192 
193     alias id function (id theReceiver, SEL theSelector, ...) pfobjc_msgSend;
194     alias id function (objc_super* superr, SEL op, ...) pfobjc_msgSendSuper;
195 
196     version (AnyX86)
197         alias void function (void* stretAddr, id theReceiver, SEL theSelector, ...) pfobjc_msgSend_stret;
198 
199     alias const(char)* function (id obj) pfobject_getClassName;
200     alias Ivar function (id obj, const(char)* name, void** outValue) pfobject_getInstanceVariable;
201     alias Ivar function (id obj, const(char)* name, void* value) pfobject_setInstanceVariable;
202     alias SEL function (const(char)* str) pfsel_registerName;
203     version (X86)
204         alias double function (id self, SEL op, ...) pfobjc_msgSend_fpret;
205 
206     alias Method function (Class aClass, const(SEL) aSelector) pfclass_getInstanceMethod;
207     alias IMP function (Method method, IMP imp) pfmethod_setImplementation;
208 
209 
210     // like pfobjc_msgSend except for returning NSPoint
211     alias NSPoint function (id theReceiver, const(SEL) theSelector, ...) pfobjc_msgSend_NSPointret;
212 
213 
214     alias pfobjc_getProtocol = Protocol* function (const(char)* name);
215     alias pfclass_addProtocol = BOOL function (Class cls, Protocol* protocol);
216     alias pfobjc_allocateProtocol = Protocol* function(const(char)* name);
217     alias pfobjc_registerProtocol = void function(Protocol *proto);
218     alias pfclass_conformsToProtocol = BOOL function(Class cls, Protocol *protocol);
219 
220     alias pfprotocol_addMethodDescription = void function(Protocol *proto, SEL name, const char *types, BOOL isRequiredMethod, BOOL isInstanceMethod);
221 }
222 
223 __gshared
224 {
225     pfobjc_registerClassPair objc_registerClassPair;
226 
227     pfclass_addIvar varclass_addIvar;
228     pfclass_addMethod varclass_addMethod;
229     pfobjc_allocateClassPair varobjc_allocateClassPair;
230     pfobjc_disposeClassPair objc_disposeClassPair;
231     pfobjc_getClass varobjc_getClass;
232     pfobjc_getMetaClass objc_getMetaClass;
233     pfobjc_lookUpClass varobjc_lookUpClass;
234 
235     pfobjc_msgSend objc_msgSend;
236     pfobjc_msgSendSuper objc_msgSendSuper;
237 
238     version(AnyX86)
239         pfobjc_msgSend_stret objc_msgSend_stret;
240 
241     version(X86)
242         pfobjc_msgSend_fpret objc_msgSend_fpret;
243 
244     pfobject_getClassName varobject_getClassName;
245     pfobject_getInstanceVariable object_getInstanceVariable;
246     pfobject_setInstanceVariable object_setInstanceVariable;
247     pfsel_registerName varsel_registerName;
248 
249     pfclass_getInstanceMethod varclass_getInstanceMethod;
250     pfmethod_setImplementation method_setImplementation;
251 
252     pfobjc_getProtocol objc_getProtocol;
253     pfclass_addProtocol class_addProtocol;
254     pfobjc_allocateProtocol objc_allocateProtocol;
255     pfobjc_registerProtocol objc_registerProtocol;
256     pfclass_conformsToProtocol class_conformsToProtocol;
257     pfprotocol_addMethodDescription protocol_addMethodDescription;
258 }
259 
260 bool class_addIvar (Class cls, string name, size_t size, byte alignment, string types) nothrow @nogc
261 {
262     return varclass_addIvar(cls, CString(name), size, alignment, CString(types));
263 }
264 
265 bool class_addMethod (Class cls, SEL name, IMP imp, string types) nothrow @nogc
266 {
267     return varclass_addMethod(cls, name, imp, CString(types));
268 }
269 
270 Class objc_allocateClassPair (Class superclass, const(char)* name, size_t extraBytes) nothrow @nogc
271 {
272     return varobjc_allocateClassPair(superclass, name, extraBytes);
273 }
274 
275 id objc_getClass (string name) nothrow @nogc
276 {
277     return varobjc_getClass(CString(name));
278 }
279 
280 id objc_getClass (char* name) nothrow @nogc
281 {
282     return varobjc_getClass(name);
283 }
284 
285 id objc_lookUpClass (string name) nothrow @nogc
286 {
287     return varobjc_lookUpClass(CString(name));
288 }
289 /*
290 string object_getClassName (id obj) nothrow @nogc
291 {
292     return fromStringz(varobject_getClassName(obj)).idup;
293 }
294 */
295 SEL sel_registerName (string str) nothrow @nogc
296 {
297     return varsel_registerName(CString(str));
298 }
299 
300 Method class_getInstanceMethod (Class aClass, string aSelector) nothrow @nogc
301 {
302     return varclass_getInstanceMethod(aClass, CString(aSelector));
303 }
304 
305 // Lazy selector literal
306 // eg: sel!"init"
307 SEL sel(string selectorName)() nothrow @nogc
308 {
309     version(useTLS)
310     {
311         // Use of TLS here
312         static size_t cached = 0;
313         if (cached == 0)
314         {
315             cached = cast(size_t)( sel_registerName(selectorName) );        
316         }
317         return cast(SEL) cached;
318     }
319     else
320     {
321         // we use type-punning here because deep shared(T) is annoying
322         shared(size_t) cached = 0;
323         size_t got = atomicLoad(cached);
324         if (got == 0)
325         {
326             got = cast(size_t)( sel_registerName(selectorName) );
327             atomicStore(cached, got);
328         }
329         return cast(SEL) got;
330     }
331 }
332 
333 // Lazy class object
334 // eg: lazyClass!"NSObject"
335 id lazyClass(string className)() nothrow @nogc
336 {
337     version(useTLS)
338     {
339         // Use of TLS here
340         static size_t cached = 0;
341         if (cached == 0)
342         {
343             cached = cast(size_t)( objc_getClass(className) );        
344         }
345         return cast(id) cached;
346     }
347     else
348     {
349         // we use type-punning here because deep shared(T) is annoying
350         shared(size_t) cached = 0;
351         size_t got = atomicLoad(cached);
352         if (got == 0)
353         {
354             got = cast(size_t)( objc_getClass(className) );
355             atomicStore(cached, got);
356         }
357         return cast(id) got;
358     }
359 }
360 
361 Protocol* lazyProtocol(string className)() nothrow @nogc
362 {
363     version(useTLS)
364     {
365         static size_t cached = 0;
366         if (cached == 0)
367         {
368             cached = cast(size_t)( objc_getProtocol(className) );        
369         }
370         return cast(Protocol*) cached;
371     }
372     else
373     {
374         // we use type-punning here because deep shared(T) is annoying
375         shared(size_t) cached = 0;
376         size_t got = atomicLoad(cached);
377         if (got == 0)
378         {
379             got = cast(size_t)( objc_getProtocol(className) );
380             atomicStore(cached, got);
381         }
382         return cast(Protocol*) got;
383     }
384 }
385 
386 // @encode replacement
387 template encode(T)
388 {
389     static if (is(T == int))
390         enum encode = "i";
391     else static if (is(T == NSRect))
392     {
393         enum encode = "{_NSRect={_NSPoint=dd}{_NSSize=dd}}";
394     }
395     else static if (is(T == NSSize))
396     {
397         enum encode = "{_NSSize=dd}";
398     }
399     else
400         static assert(false, "TODO implement encode for type " ~ T.stringof);
401 }