1 /** 2 Objective-C runtime trickery for interfacing. 3 4 Copyright: Guillaume Piolat 2016. 5 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 7 Unknown licence from: 8 9 Initial Version: Darrell Walisser <dwaliss1@purdue.edu> 10 Non-NIB-Code & other changes: Max Horn <max@quendi.de> 11 Port to the D programming language: Jacob Carlborg <jacob.carlborg@gmail.com> 12 Resurrected by: Guillaume Piolat <contact@auburnsounds.com> for the purpose of audio plug-ins 13 14 It just says "Feel free to customize this file for your purpose" 15 16 TODO ask original authors of the runtime trickery for a licence 17 */ 18 module derelict.cocoa.runtime; 19 20 21 /// Important reading: The "OS X ABI Function Call Guide" 22 /// https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/ 23 24 import core.stdc.config; 25 import core.atomic; 26 27 import std.string; 28 29 import dplug.core.nogc; 30 31 32 version(X86) 33 version = AnyX86; 34 version(X86_64) 35 version = AnyX86; 36 37 //version = useTLS; 38 39 // NSGeometry.h 40 41 alias NSInteger = ptrdiff_t; 42 alias NSUInteger = size_t; 43 44 static if ((void*).sizeof > int.sizeof) // 64bit 45 alias CGFloat = double; 46 else 47 alias CGFloat = float; 48 49 struct NSPoint 50 { 51 CGFloat x; 52 CGFloat y; 53 } 54 55 struct NSRange 56 { 57 NSUInteger location; 58 NSUInteger length; 59 } 60 61 struct NSRect 62 { 63 NSPoint origin; 64 NSSize size; 65 } 66 67 NSRect NSMakeRect(CGFloat x, CGFloat y, CGFloat w, CGFloat h) nothrow @nogc 68 { 69 return NSRect(NSPoint(x, y), NSSize(w, h)); 70 } 71 72 struct NSSize 73 { 74 CGFloat width; 75 CGFloat height; 76 } 77 78 alias SEL = char*; 79 alias Class = objc_class*; 80 alias id = objc_object*; 81 82 83 alias BOOL = char; 84 enum : BOOL 85 { 86 NO = 0, 87 YES = 1 88 } 89 90 alias Ivar = objc_ivar*; 91 alias Method = objc_method*; 92 alias Protocol = objc_object; 93 94 95 96 alias IMP = extern (C) id function(id, SEL, ...); 97 98 struct objc_object 99 { 100 Class isa; 101 } 102 103 struct objc_super 104 { 105 id receiver; 106 Class clazz; 107 } 108 109 struct objc_class 110 { 111 Class isa; 112 Class super_class; 113 const char* name; 114 c_long versionn; 115 c_long info; 116 c_long instance_size; 117 objc_ivar_list* ivars; 118 objc_method_list** methodLists; 119 objc_cache* cache; 120 objc_protocol_list* protocols; 121 } 122 123 alias objc_property_t = void*; 124 125 struct objc_ivar 126 { 127 char* ivar_name; 128 char* ivar_type; 129 int ivar_offset; 130 131 version (X86_64) 132 int space; 133 } 134 135 struct objc_ivar_list 136 { 137 int ivar_count; 138 139 version (X86_64) 140 int space; 141 142 /* variable length structure */ 143 objc_ivar[1] ivar_list; 144 } 145 146 struct objc_method 147 { 148 SEL method_name; 149 char* method_types; 150 IMP method_imp; 151 } 152 153 struct objc_method_list 154 { 155 objc_method_list* obsolete; 156 157 int method_count; 158 159 version (X86_64) 160 int space; 161 162 /* variable length structure */ 163 objc_method[1] method_list; 164 } 165 166 struct objc_cache 167 { 168 uint mask /* total = mask + 1 */; 169 uint occupied; 170 Method[1] buckets; 171 } 172 173 struct objc_protocol_list 174 { 175 objc_protocol_list* next; 176 long count; 177 Protocol*[1] list; 178 } 179 180 //Objective-C runtime bindings from the Cocoa framework 181 extern (C) nothrow @nogc 182 { 183 alias Class function (Class superclass) pfobjc_registerClassPair; 184 185 alias bool function (Class cls, const(char)* name, size_t size, byte alignment, const(char)* types) pfclass_addIvar; 186 alias bool function (Class cls, const(SEL) name, IMP imp, const(char)* types) pfclass_addMethod; 187 alias Class function (Class superclass, const(char)* name, size_t extraBytes) pfobjc_allocateClassPair; 188 alias void function(Class cls) pfobjc_disposeClassPair; 189 alias id function (const(char)* name) pfobjc_getClass; 190 alias id function (const(char)* name) pfobjc_lookUpClass; 191 192 alias id function (id theReceiver, SEL theSelector, ...) pfobjc_msgSend; 193 alias id function (objc_super* superr, SEL op, ...) pfobjc_msgSendSuper; 194 195 version (AnyX86) 196 alias void function (void* stretAddr, id theReceiver, SEL theSelector, ...) pfobjc_msgSend_stret; 197 198 alias const(char)* function (id obj) pfobject_getClassName; 199 alias Ivar function (id obj, const(char)* name, void** outValue) pfobject_getInstanceVariable; 200 alias Ivar function (id obj, const(char)* name, void* value) pfobject_setInstanceVariable; 201 alias SEL function (const(char)* str) pfsel_registerName; 202 version (X86) 203 alias double function (id self, SEL op, ...) pfobjc_msgSend_fpret; 204 205 alias Method function (Class aClass, const(SEL) aSelector) pfclass_getInstanceMethod; 206 alias IMP function (Method method, IMP imp) pfmethod_setImplementation; 207 208 209 // like pfobjc_msgSend except for returning NSPoint 210 alias NSPoint function (id theReceiver, const(SEL) theSelector, ...) pfobjc_msgSend_NSPointret; 211 212 213 alias pfobjc_getProtocol = Protocol* function (const(char)* name); 214 alias pfclass_addProtocol = BOOL function (Class cls, Protocol* protocol); 215 alias pfobjc_allocateProtocol = Protocol* function(const(char)* name); 216 alias pfobjc_registerProtocol = void function(Protocol *proto); 217 alias pfclass_conformsToProtocol = BOOL function(Class cls, Protocol *protocol); 218 219 alias pfprotocol_addMethodDescription = void function(Protocol *proto, SEL name, const char *types, BOOL isRequiredMethod, BOOL isInstanceMethod); 220 } 221 222 __gshared 223 { 224 pfobjc_registerClassPair objc_registerClassPair; 225 226 pfclass_addIvar varclass_addIvar; 227 pfclass_addMethod varclass_addMethod; 228 pfobjc_allocateClassPair varobjc_allocateClassPair; 229 pfobjc_disposeClassPair objc_disposeClassPair; 230 pfobjc_getClass varobjc_getClass; 231 pfobjc_lookUpClass varobjc_lookUpClass; 232 233 pfobjc_msgSend objc_msgSend; 234 pfobjc_msgSendSuper objc_msgSendSuper; 235 236 version(AnyX86) 237 pfobjc_msgSend_stret objc_msgSend_stret; 238 239 version(X86) 240 pfobjc_msgSend_fpret objc_msgSend_fpret; 241 242 pfobject_getClassName varobject_getClassName; 243 pfobject_getInstanceVariable object_getInstanceVariable; 244 pfobject_setInstanceVariable object_setInstanceVariable; 245 pfsel_registerName varsel_registerName; 246 247 pfclass_getInstanceMethod varclass_getInstanceMethod; 248 pfmethod_setImplementation method_setImplementation; 249 250 pfobjc_getProtocol objc_getProtocol; 251 pfclass_addProtocol class_addProtocol; 252 pfobjc_allocateProtocol objc_allocateProtocol; 253 pfobjc_registerProtocol objc_registerProtocol; 254 pfclass_conformsToProtocol class_conformsToProtocol; 255 pfprotocol_addMethodDescription protocol_addMethodDescription; 256 } 257 258 bool class_addIvar (Class cls, string name, size_t size, byte alignment, string types) nothrow @nogc 259 { 260 CString nameZ = CString(name); 261 CString typesZ = CString(types); 262 return varclass_addIvar(cls, nameZ.storage, size, alignment, typesZ.storage); 263 } 264 265 bool class_addMethod (Class cls, SEL name, IMP imp, string types) nothrow @nogc 266 { 267 CString typesZ = CString(types); 268 return varclass_addMethod(cls, name, imp, typesZ.storage); 269 } 270 271 Class objc_allocateClassPair (Class superclass, const(char)* name, size_t extraBytes) nothrow @nogc 272 { 273 return varobjc_allocateClassPair(superclass, name, extraBytes); 274 } 275 276 id objc_getClass (string name) nothrow @nogc 277 { 278 CString nameZ = CString(name); 279 return varobjc_getClass(nameZ.storage); 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 CString nameZ = CString(name); 290 return varobjc_lookUpClass(nameZ.storage); 291 } 292 /* 293 string object_getClassName (id obj) nothrow @nogc 294 { 295 return fromStringz(varobject_getClassName(obj)).idup; 296 } 297 */ 298 SEL sel_registerName (string str) nothrow @nogc 299 { 300 CString strZ = CString(str); 301 return varsel_registerName(strZ.storage); 302 } 303 304 Method class_getInstanceMethod (Class aClass, string aSelector) nothrow @nogc 305 { 306 CString aSelectorZ = CString(aSelector); 307 return varclass_getInstanceMethod(aClass, aSelectorZ.storage); 308 } 309 310 // Lazy selector literal 311 // eg: sel!"init" 312 SEL sel(string selectorName)() nothrow @nogc 313 { 314 // PERF: too much allocation here 315 version(useTLS) 316 { 317 // Use of TLS here 318 static size_t cached = 0; 319 if (cached == 0) 320 { 321 cached = cast(size_t)( sel_registerName(selectorName) ); 322 } 323 return cast(SEL) cached; 324 } 325 else 326 { 327 // we use type-punning here because deep shared(T) is annoying 328 shared(size_t) cached = 0; 329 size_t got = atomicLoad(cached); 330 if (got == 0) 331 { 332 got = cast(size_t)( sel_registerName(selectorName) ); 333 atomicStore(cached, got); 334 } 335 return cast(SEL) got; 336 } 337 } 338 339 // Lazy class object 340 // eg: lazyClass!"NSObject" 341 id lazyClass(string className)() nothrow @nogc 342 { 343 // PERF: too much allocation here 344 version(useTLS) 345 { 346 // Use of TLS here 347 static size_t cached = 0; 348 if (cached == 0) 349 { 350 cached = cast(size_t)( objc_getClass(className) ); 351 } 352 return cast(id) cached; 353 } 354 else 355 { 356 // we use type-punning here because deep shared(T) is annoying 357 shared(size_t) cached = 0; 358 size_t got = atomicLoad(cached); 359 if (got == 0) 360 { 361 got = cast(size_t)( objc_getClass(className) ); 362 atomicStore(cached, got); 363 } 364 return cast(id) got; 365 } 366 } 367 368 Protocol* lazyProtocol(string className)() nothrow @nogc 369 { 370 // PERF: too much allocation here 371 version(useTLS) 372 { 373 static size_t cached = 0; 374 if (cached == 0) 375 { 376 cached = cast(size_t)( objc_getProtocol(className) ); 377 } 378 return cast(Protocol*) cached; 379 } 380 else 381 { 382 // we use type-punning here because deep shared(T) is annoying 383 shared(size_t) cached = 0; 384 size_t got = atomicLoad(cached); 385 if (got == 0) 386 { 387 got = cast(size_t)( objc_getProtocol(className) ); 388 atomicStore(cached, got); 389 } 390 return cast(Protocol*) got; 391 } 392 } 393 394 // @encode replacement 395 template encode(T) 396 { 397 static if (is(T == int)) 398 enum encode = "i"; 399 else static if (is(T == NSRect)) 400 { 401 enum encode = "{_NSRect={_NSPoint=dd}{_NSSize=dd}}"; 402 } 403 else static if (is(T == NSSize)) 404 { 405 enum encode = "{_NSSize=dd}"; 406 } 407 else 408 static assert(false, "TODO implement encode for type " ~ T.stringof); 409 }