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 }