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 }