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 }