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