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 return varclass_addIvar(cls, CString(name), size, alignment, CString(types)); 261 } 262 263 bool class_addMethod (Class cls, SEL name, IMP imp, string types) nothrow @nogc 264 { 265 return varclass_addMethod(cls, name, imp, CString(types)); 266 } 267 268 Class objc_allocateClassPair (Class superclass, const(char)* name, size_t extraBytes) nothrow @nogc 269 { 270 return varobjc_allocateClassPair(superclass, name, extraBytes); 271 } 272 273 id objc_getClass (string name) nothrow @nogc 274 { 275 return varobjc_getClass(CString(name)); 276 } 277 278 id objc_getClass (char* name) nothrow @nogc 279 { 280 return varobjc_getClass(name); 281 } 282 283 id objc_lookUpClass (string name) nothrow @nogc 284 { 285 return varobjc_lookUpClass(CString(name)); 286 } 287 /* 288 string object_getClassName (id obj) nothrow @nogc 289 { 290 return fromStringz(varobject_getClassName(obj)).idup; 291 } 292 */ 293 SEL sel_registerName (string str) nothrow @nogc 294 { 295 return varsel_registerName(CString(str)); 296 } 297 298 Method class_getInstanceMethod (Class aClass, string aSelector) nothrow @nogc 299 { 300 return varclass_getInstanceMethod(aClass, CString(aSelector)); 301 } 302 303 // Lazy selector literal 304 // eg: sel!"init" 305 SEL sel(string selectorName)() nothrow @nogc 306 { 307 version(useTLS) 308 { 309 // Use of TLS here 310 static size_t cached = 0; 311 if (cached == 0) 312 { 313 cached = cast(size_t)( sel_registerName(selectorName) ); 314 } 315 return cast(SEL) cached; 316 } 317 else 318 { 319 // we use type-punning here because deep shared(T) is annoying 320 shared(size_t) cached = 0; 321 size_t got = atomicLoad(cached); 322 if (got == 0) 323 { 324 got = cast(size_t)( sel_registerName(selectorName) ); 325 atomicStore(cached, got); 326 } 327 return cast(SEL) got; 328 } 329 } 330 331 // Lazy class object 332 // eg: lazyClass!"NSObject" 333 id lazyClass(string className)() nothrow @nogc 334 { 335 version(useTLS) 336 { 337 // Use of TLS here 338 static size_t cached = 0; 339 if (cached == 0) 340 { 341 cached = cast(size_t)( objc_getClass(className) ); 342 } 343 return cast(id) cached; 344 } 345 else 346 { 347 // we use type-punning here because deep shared(T) is annoying 348 shared(size_t) cached = 0; 349 size_t got = atomicLoad(cached); 350 if (got == 0) 351 { 352 got = cast(size_t)( objc_getClass(className) ); 353 atomicStore(cached, got); 354 } 355 return cast(id) got; 356 } 357 } 358 359 Protocol* lazyProtocol(string className)() nothrow @nogc 360 { 361 version(useTLS) 362 { 363 static size_t cached = 0; 364 if (cached == 0) 365 { 366 cached = cast(size_t)( objc_getProtocol(className) ); 367 } 368 return cast(Protocol*) cached; 369 } 370 else 371 { 372 // we use type-punning here because deep shared(T) is annoying 373 shared(size_t) cached = 0; 374 size_t got = atomicLoad(cached); 375 if (got == 0) 376 { 377 got = cast(size_t)( objc_getProtocol(className) ); 378 atomicStore(cached, got); 379 } 380 return cast(Protocol*) got; 381 } 382 } 383 384 // @encode replacement 385 template encode(T) 386 { 387 static if (is(T == int)) 388 enum encode = "i"; 389 else static if (is(T == NSRect)) 390 { 391 enum encode = "{_NSRect={_NSPoint=dd}{_NSSize=dd}}"; 392 } 393 else static if (is(T == NSSize)) 394 { 395 enum encode = "{_NSSize=dd}"; 396 } 397 else 398 static assert(false, "TODO implement encode for type " ~ T.stringof); 399 }