1 /*
2 * Copyright (c) 2004-2015 Derelict Developers
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 *   notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 *   notice, this list of conditions and the following disclaimer in the
14 *   documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the names 'Derelict', 'DerelictSDL', nor the names of its contributors
17 *   may be used to endorse or promote products derived from this software
18 *   without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 module derelict.cocoa.runtime;
33 
34 
35 /// Important reading: The "OS X ABI Function Call Guide"
36 /// https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/
37 
38 import core.stdc.config;
39 import core.atomic;
40 
41 import std.string;
42 
43 import dplug.core.nogc;
44 
45 
46 
47 // NSGeometry.h
48 
49 alias NSInteger = ptrdiff_t;
50 alias NSUInteger = size_t;
51 
52 static if ((void*).sizeof > int.sizeof) // 64bit
53     alias CGFloat = double;
54 else
55     alias CGFloat = float;
56 
57 struct NSPoint
58 {
59     CGFloat x;
60     CGFloat y;
61 }
62 
63 struct NSRange
64 {
65     NSUInteger location;
66     NSUInteger length;
67 }
68 
69 struct NSRect
70 {
71     NSPoint origin;
72     NSSize size;
73 }
74 
75 NSRect NSMakeRect(CGFloat x, CGFloat y, CGFloat w, CGFloat h) nothrow @nogc
76 {
77     return NSRect(NSPoint(x, y), NSSize(w, h));
78 }
79 
80 struct NSSize
81 {
82     CGFloat width;
83     CGFloat height;
84 }
85 
86 alias SEL = char*;
87 alias Class = objc_class*;
88 alias id = objc_object*;
89 
90 
91 alias BOOL = char;
92 enum : BOOL
93 {
94     NO = 0,
95     YES = 1
96 }
97 
98 alias Ivar = objc_ivar*;
99 alias Method = objc_method*;
100 alias Protocol = objc_object;
101 
102 
103 
104 alias IMP = extern (C) id function(id, SEL, ...);
105 
106 struct objc_object
107 {
108     Class isa;
109 }
110 
111 struct objc_super
112 {
113     id receiver;
114     Class clazz;
115 }
116 
117 struct objc_class
118 {
119     Class isa;
120     Class super_class;
121     const char* name;
122     c_long versionn;
123     c_long info;
124     c_long instance_size;
125     objc_ivar_list* ivars;
126     objc_method_list** methodLists;
127     objc_cache* cache;
128     objc_protocol_list* protocols;
129 }
130 
131 alias objc_property_t = void*;
132 
133 struct objc_ivar
134 {
135     char* ivar_name;
136     char* ivar_type;
137     int ivar_offset;
138 
139     version (X86_64)
140         int space;
141 }
142 
143 struct objc_ivar_list
144 {
145     int ivar_count;
146 
147     version (X86_64)
148         int space;
149 
150     /* variable length structure */
151     objc_ivar[1] ivar_list;
152 }
153 
154 struct objc_method
155 {
156     SEL method_name;
157     char* method_types;
158     IMP method_imp;
159 }
160 
161 struct objc_method_list
162 {
163     objc_method_list* obsolete;
164 
165     int method_count;
166 
167     version (X86_64)
168         int space;
169 
170     /* variable length structure */
171     objc_method[1] method_list;
172 }
173 
174 struct objc_cache
175 {
176     uint mask /* total = mask + 1 */;
177     uint occupied;
178     Method[1] buckets;
179 }
180 
181 struct objc_protocol_list
182 {
183     objc_protocol_list* next;
184     long count;
185     Protocol*[1] list;
186 }
187 
188 //Objective-C runtime bindings from the Cocoa framework
189 extern (C) nothrow @nogc
190 {
191     alias Class function (Class superclass) pfobjc_registerClassPair;
192 
193     alias bool function (Class cls, const(char)* name, size_t size, byte alignment, const(char)* types) pfclass_addIvar;
194     alias bool function (Class cls, const(SEL) name, IMP imp, const(char)* types) pfclass_addMethod;
195     alias Class function (Class superclass, const(char)* name, size_t extraBytes) pfobjc_allocateClassPair;
196     alias void function(Class cls) pfobjc_disposeClassPair;
197     alias id function (const(char)* name) pfobjc_getClass;
198     alias id function (const(char)* name) pfobjc_lookUpClass;
199 
200     alias id function (id theReceiver, SEL theSelector, ...) pfobjc_msgSend;
201     alias id function (objc_super* superr, SEL op, ...) pfobjc_msgSendSuper;
202     alias void function (void* stretAddr, id theReceiver, SEL theSelector, ...) pfobjc_msgSend_stret;
203 
204     alias const(char)* function (id obj) pfobject_getClassName;
205     alias Ivar function (id obj, const(char)* name, void** outValue) pfobject_getInstanceVariable;
206     alias Ivar function (id obj, const(char)* name, void* value) pfobject_setInstanceVariable;
207     alias SEL function (const(char)* str) pfsel_registerName;
208     version (X86)
209         alias double function (id self, SEL op, ...) pfobjc_msgSend_fpret;
210 
211     alias Method function (Class aClass, const(SEL) aSelector) pfclass_getInstanceMethod;
212     alias IMP function (Method method, IMP imp) pfmethod_setImplementation;
213 
214 
215     // like pfobjc_msgSend except for returning NSPoint
216     alias NSPoint function (id theReceiver, const(SEL) theSelector, ...) pfobjc_msgSend_NSPointret;
217 
218 
219     alias pfobjc_getProtocol = Protocol* function (const(char)* name);
220     alias pfclass_addProtocol = BOOL function (Class cls, Protocol* protocol);
221     alias pfobjc_allocateProtocol = Protocol* function(const(char)* name);
222     alias pfobjc_registerProtocol = void function(Protocol *proto);
223     alias pfclass_conformsToProtocol = BOOL function(Class cls, Protocol *protocol);
224 
225     alias pfprotocol_addMethodDescription = void function(Protocol *proto, SEL name, const char *types, BOOL isRequiredMethod, BOOL isInstanceMethod);
226 }
227 
228 __gshared
229 {
230     pfobjc_registerClassPair objc_registerClassPair;
231 
232     pfclass_addIvar varclass_addIvar;
233     pfclass_addMethod varclass_addMethod;
234     pfobjc_allocateClassPair varobjc_allocateClassPair;
235     pfobjc_disposeClassPair objc_disposeClassPair;
236     pfobjc_getClass varobjc_getClass;
237     pfobjc_lookUpClass varobjc_lookUpClass;
238 
239     pfobjc_msgSend objc_msgSend;
240     pfobjc_msgSendSuper objc_msgSendSuper;
241     pfobjc_msgSend_stret objc_msgSend_stret;
242 
243     version(X86)
244         pfobjc_msgSend_fpret objc_msgSend_fpret;
245 
246     pfobject_getClassName varobject_getClassName;
247     pfobject_getInstanceVariable object_getInstanceVariable;
248     pfobject_setInstanceVariable object_setInstanceVariable;
249     pfsel_registerName varsel_registerName;
250 
251     pfclass_getInstanceMethod varclass_getInstanceMethod;
252     pfmethod_setImplementation method_setImplementation;
253 
254     pfobjc_getProtocol objc_getProtocol;
255     pfclass_addProtocol class_addProtocol;
256     pfobjc_allocateProtocol objc_allocateProtocol;
257     pfobjc_registerProtocol objc_registerProtocol;
258     pfclass_conformsToProtocol class_conformsToProtocol;
259     pfprotocol_addMethodDescription protocol_addMethodDescription;
260 }
261 
262 bool class_addIvar (Class cls, string name, size_t size, byte alignment, string types) nothrow @nogc
263 {
264     return varclass_addIvar(cls, CString(name), size, alignment, CString(types));
265 }
266 
267 bool class_addMethod (Class cls, SEL name, IMP imp, string types) nothrow @nogc
268 {
269     return varclass_addMethod(cls, name, imp, CString(types));
270 }
271 
272 Class objc_allocateClassPair (Class superclass, const(char)* name, size_t extraBytes) nothrow @nogc
273 {
274     return varobjc_allocateClassPair(superclass, name, extraBytes);
275 }
276 
277 id objc_getClass (string name) nothrow @nogc
278 {
279     return varobjc_getClass(CString(name));
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     return varobjc_lookUpClass(CString(name));
290 }
291 /*
292 string object_getClassName (id obj) nothrow @nogc
293 {
294     return fromStringz(varobject_getClassName(obj)).idup;
295 }
296 */
297 SEL sel_registerName (string str) nothrow @nogc
298 {
299     return varsel_registerName(CString(str));
300 }
301 
302 Method class_getInstanceMethod (Class aClass, string aSelector) nothrow @nogc
303 {
304     return varclass_getInstanceMethod(aClass, CString(aSelector));
305 }
306 
307 // Lazy selector literal
308 // eg: sel!"init"
309 SEL sel(string selectorName)() nothrow @nogc
310 {
311     // we use type-punning here because deep shared(T) is annoying
312     shared(size_t) cached = 0;
313     size_t got = atomicLoad(cached);
314     if (got == 0)
315     {
316         got = cast(size_t)( sel_registerName(selectorName) );
317         atomicStore(cached, got);
318     }
319     return cast(SEL) got;
320 }
321 
322 // Lazy class object
323 // eg: lazyClass!"NSObject"
324 id lazyClass(string className)() nothrow @nogc
325 {
326     // we use type-punning here because deep shared(T) is annoying
327     shared(size_t) cached = 0;
328     size_t got = atomicLoad(cached);
329     if (got == 0)
330     {
331         got = cast(size_t)( objc_getClass(className) );
332         atomicStore(cached, got);
333     }
334     return cast(id) got;
335 }
336 
337 Protocol* lazyProtocol(string className)() nothrow @nogc
338 {
339     // we use type-punning here because deep shared(T) is annoying
340     shared(size_t) cached = 0;
341     size_t got = atomicLoad(cached);
342     if (got == 0)
343     {
344         got = cast(size_t)( objc_getProtocol(className) );
345         atomicStore(cached, got);
346     }
347     return cast(Protocol*) got;
348 }
349 
350 // @encode replacement
351 template encode(T)
352 {
353     static if (is(T == int))
354         enum encode = "i";
355     else static if (is(T == NSRect))
356     {
357         enum encode = "{_NSRect={_NSPoint=dd}{_NSSize=dd}}";
358     }
359     else static if (is(T == NSSize))
360     {
361         enum encode = "{_NSSize=dd}";
362     }
363     else
364         static assert(false, "TODO implement encode for type " ~ T.stringof);
365 }