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 bindbc.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_getMetaClass; 191 alias id function (const(char)* name) pfobjc_lookUpClass; 192 193 alias id function (id theReceiver, SEL theSelector, ...) pfobjc_msgSend; 194 alias id function (objc_super* superr, SEL op, ...) pfobjc_msgSendSuper; 195 196 version (AnyX86) 197 alias void function (void* stretAddr, id theReceiver, SEL theSelector, ...) pfobjc_msgSend_stret; 198 199 alias const(char)* function (id obj) pfobject_getClassName; 200 alias Ivar function (id obj, const(char)* name, void** outValue) pfobject_getInstanceVariable; 201 alias Ivar function (id obj, const(char)* name, void* value) pfobject_setInstanceVariable; 202 alias SEL function (const(char)* str) pfsel_registerName; 203 version (X86) 204 alias double function (id self, SEL op, ...) pfobjc_msgSend_fpret; 205 206 alias Method function (Class aClass, const(SEL) aSelector) pfclass_getInstanceMethod; 207 alias IMP function (Method method, IMP imp) pfmethod_setImplementation; 208 209 210 // like pfobjc_msgSend except for returning NSPoint 211 alias NSPoint function (id theReceiver, const(SEL) theSelector, ...) pfobjc_msgSend_NSPointret; 212 213 214 alias pfobjc_getProtocol = Protocol* function (const(char)* name); 215 alias pfclass_addProtocol = BOOL function (Class cls, Protocol* protocol); 216 alias pfobjc_allocateProtocol = Protocol* function(const(char)* name); 217 alias pfobjc_registerProtocol = void function(Protocol *proto); 218 alias pfclass_conformsToProtocol = BOOL function(Class cls, Protocol *protocol); 219 220 alias pfprotocol_addMethodDescription = void function(Protocol *proto, SEL name, const char *types, BOOL isRequiredMethod, BOOL isInstanceMethod); 221 } 222 223 __gshared 224 { 225 pfobjc_registerClassPair objc_registerClassPair; 226 227 pfclass_addIvar varclass_addIvar; 228 pfclass_addMethod varclass_addMethod; 229 pfobjc_allocateClassPair varobjc_allocateClassPair; 230 pfobjc_disposeClassPair objc_disposeClassPair; 231 pfobjc_getClass varobjc_getClass; 232 pfobjc_getMetaClass objc_getMetaClass; 233 pfobjc_lookUpClass varobjc_lookUpClass; 234 235 pfobjc_msgSend objc_msgSend; 236 pfobjc_msgSendSuper objc_msgSendSuper; 237 238 version(AnyX86) 239 pfobjc_msgSend_stret objc_msgSend_stret; 240 241 version(X86) 242 pfobjc_msgSend_fpret objc_msgSend_fpret; 243 244 pfobject_getClassName varobject_getClassName; 245 pfobject_getInstanceVariable object_getInstanceVariable; 246 pfobject_setInstanceVariable object_setInstanceVariable; 247 pfsel_registerName varsel_registerName; 248 249 pfclass_getInstanceMethod varclass_getInstanceMethod; 250 pfmethod_setImplementation method_setImplementation; 251 252 pfobjc_getProtocol objc_getProtocol; 253 pfclass_addProtocol class_addProtocol; 254 pfobjc_allocateProtocol objc_allocateProtocol; 255 pfobjc_registerProtocol objc_registerProtocol; 256 pfclass_conformsToProtocol class_conformsToProtocol; 257 pfprotocol_addMethodDescription protocol_addMethodDescription; 258 } 259 260 bool class_addIvar (Class cls, string name, size_t size, byte alignment, string types) nothrow @nogc 261 { 262 return varclass_addIvar(cls, CString(name), size, alignment, CString(types)); 263 } 264 265 bool class_addMethod (Class cls, SEL name, IMP imp, string types) nothrow @nogc 266 { 267 return varclass_addMethod(cls, name, imp, CString(types)); 268 } 269 270 Class objc_allocateClassPair (Class superclass, const(char)* name, size_t extraBytes) nothrow @nogc 271 { 272 return varobjc_allocateClassPair(superclass, name, extraBytes); 273 } 274 275 id objc_getClass (string name) nothrow @nogc 276 { 277 return varobjc_getClass(CString(name)); 278 } 279 280 id objc_getClass (char* name) nothrow @nogc 281 { 282 return varobjc_getClass(name); 283 } 284 285 id objc_lookUpClass (string name) nothrow @nogc 286 { 287 return varobjc_lookUpClass(CString(name)); 288 } 289 /* 290 string object_getClassName (id obj) nothrow @nogc 291 { 292 return fromStringz(varobject_getClassName(obj)).idup; 293 } 294 */ 295 SEL sel_registerName (string str) nothrow @nogc 296 { 297 return varsel_registerName(CString(str)); 298 } 299 300 Method class_getInstanceMethod (Class aClass, string aSelector) nothrow @nogc 301 { 302 return varclass_getInstanceMethod(aClass, CString(aSelector)); 303 } 304 305 // Lazy selector literal 306 // eg: sel!"init" 307 SEL sel(string selectorName)() nothrow @nogc 308 { 309 version(useTLS) 310 { 311 // Use of TLS here 312 static size_t cached = 0; 313 if (cached == 0) 314 { 315 cached = cast(size_t)( sel_registerName(selectorName) ); 316 } 317 return cast(SEL) cached; 318 } 319 else 320 { 321 // we use type-punning here because deep shared(T) is annoying 322 shared(size_t) cached = 0; 323 size_t got = atomicLoad(cached); 324 if (got == 0) 325 { 326 got = cast(size_t)( sel_registerName(selectorName) ); 327 atomicStore(cached, got); 328 } 329 return cast(SEL) got; 330 } 331 } 332 333 // Lazy class object 334 // eg: lazyClass!"NSObject" 335 id lazyClass(string className)() nothrow @nogc 336 { 337 version(useTLS) 338 { 339 // Use of TLS here 340 static size_t cached = 0; 341 if (cached == 0) 342 { 343 cached = cast(size_t)( objc_getClass(className) ); 344 } 345 return cast(id) cached; 346 } 347 else 348 { 349 // we use type-punning here because deep shared(T) is annoying 350 shared(size_t) cached = 0; 351 size_t got = atomicLoad(cached); 352 if (got == 0) 353 { 354 got = cast(size_t)( objc_getClass(className) ); 355 atomicStore(cached, got); 356 } 357 return cast(id) got; 358 } 359 } 360 361 Protocol* lazyProtocol(string className)() nothrow @nogc 362 { 363 version(useTLS) 364 { 365 static size_t cached = 0; 366 if (cached == 0) 367 { 368 cached = cast(size_t)( objc_getProtocol(className) ); 369 } 370 return cast(Protocol*) cached; 371 } 372 else 373 { 374 // we use type-punning here because deep shared(T) is annoying 375 shared(size_t) cached = 0; 376 size_t got = atomicLoad(cached); 377 if (got == 0) 378 { 379 got = cast(size_t)( objc_getProtocol(className) ); 380 atomicStore(cached, got); 381 } 382 return cast(Protocol*) got; 383 } 384 } 385 386 // @encode replacement 387 template encode(T) 388 { 389 static if (is(T == int)) 390 enum encode = "i"; 391 else static if (is(T == NSRect)) 392 { 393 enum encode = "{_NSRect={_NSPoint=dd}{_NSSize=dd}}"; 394 } 395 else static if (is(T == NSSize)) 396 { 397 enum encode = "{_NSSize=dd}"; 398 } 399 else 400 static assert(false, "TODO implement encode for type " ~ T.stringof); 401 }