1 /** 2 * Glue code for Objective-C interop. 3 * 4 * Copyright: Copyright (C) 2015-2023 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/objc_glue.d, _objc_glue.d) 8 * Documentation: https://dlang.org/phobos/dmd_objc_glue.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/objc_glue.d 10 */ 11 12 module dmd.objc_glue; 13 14 import core.stdc.stdio; 15 import core.stdc.stdlib; 16 import core.stdc.string; 17 18 import dmd.aggregate; 19 import dmd.arraytypes; 20 import dmd.astenums; 21 import dmd.dclass; 22 import dmd.declaration; 23 import dmd.dmodule; 24 import dmd.dsymbol; 25 import dmd.expression; 26 import dmd.func; 27 import dmd.glue; 28 import dmd.identifier; 29 import dmd.mtype; 30 import dmd.objc; 31 import dmd.target; 32 33 import dmd.root.stringtable; 34 import dmd.root.array; 35 36 import dmd.backend.dt; 37 import dmd.backend.cc; 38 import dmd.backend.cdef; 39 import dmd.backend.el; 40 import dmd.backend.global; 41 import dmd.backend.oper; 42 import dmd.backend.ty; 43 import dmd.backend.type; 44 import dmd.backend.mach; 45 import dmd.backend.obj; 46 47 private __gshared ObjcGlue _objc; 48 49 ObjcGlue objc() 50 { 51 return _objc; 52 } 53 54 // Should be an interface 55 extern(C++) abstract class ObjcGlue 56 { 57 static struct ElemResult 58 { 59 elem* ec; 60 elem* ethis; 61 } 62 63 static void initialize() 64 { 65 if (target.objc.supported) 66 _objc = new Supported; 67 else 68 _objc = new Unsupported; 69 } 70 71 /// Resets the Objective-C glue layer. 72 abstract void reset(); 73 74 abstract void setupMethodSelector(FuncDeclaration fd, elem** esel); 75 76 abstract ElemResult setupMethodCall(FuncDeclaration fd, TypeFunction tf, 77 bool directcall, elem* ec, elem* ehidden, elem* ethis); 78 79 abstract void setupEp(elem* esel, elem** ep, int leftToRight); 80 abstract void generateModuleInfo(Module module_); 81 82 /// Returns: the given expression converted to an `elem` structure 83 abstract elem* toElem(ObjcClassReferenceExp e) const; 84 85 /// Outputs the given Objective-C class to the object file. 86 abstract void toObjFile(ClassDeclaration classDeclaration) const; 87 88 /** 89 * Adds the selector parameter to the given list of parameters. 90 * 91 * For Objective-C methods the selector parameter is added. For 92 * non-Objective-C methods `parameters` is unchanged. 93 * 94 * Params: 95 * functionDeclaration = the function declaration to add the selector 96 * parameter from 97 * parameters = the list of parameters to add the selector parameter to 98 * parameterCount = the number of parameters 99 * 100 * Returns: the new number of parameters 101 */ 102 abstract size_t addSelectorParameterSymbol( 103 FuncDeclaration functionDeclaration, 104 Symbol** parameters, size_t parameterCount) const; 105 106 /** 107 * Returns the offset of the given variable declaration `var`. 108 * 109 * This is used in a `DotVarExp` to get the offset of the variable the 110 * expression is accessing. 111 * 112 * Instance variables in Objective-C are non-fragile. That means that the 113 * base class can change (add or remove instance variables) without the 114 * subclasses needing to recompile or relink. This is implemented instance 115 * variables having a dynamic offset. This is achieved by going through an 116 * indirection in the form of a symbol generated in the binary. The compiler 117 * outputs the static offset in the generated symbol. Then, at load time, 118 * the symbol is updated with the correct offset, if necessary. 119 * 120 * Params: 121 * var = the variable declaration to return the offset of 122 * type = the type of the `DotVarExp` 123 * offset = the existing offset 124 * 125 * Returns: a symbol containing the offset of the variable declaration 126 */ 127 abstract elem* getOffset(VarDeclaration var, Type type, elem* offset) const; 128 } 129 130 private: 131 132 extern(C++) final class Unsupported : ObjcGlue 133 { 134 override void reset() 135 { 136 // noop 137 } 138 139 override void setupMethodSelector(FuncDeclaration fd, elem** esel) 140 { 141 // noop 142 } 143 144 override ElemResult setupMethodCall(FuncDeclaration, TypeFunction, bool, 145 elem*, elem*, elem*) 146 { 147 assert(0, "Should never be called when Objective-C is not supported"); 148 } 149 150 override void setupEp(elem* esel, elem** ep, int reverse) 151 { 152 // noop 153 } 154 155 override void generateModuleInfo(Module) 156 { 157 // noop 158 } 159 160 override elem* toElem(ObjcClassReferenceExp e) const 161 { 162 assert(0, "Should never be called when Objective-C is not supported"); 163 } 164 165 override void toObjFile(ClassDeclaration classDeclaration) const 166 { 167 assert(0, "Should never be called when Objective-C is not supported"); 168 } 169 170 override size_t addSelectorParameterSymbol(FuncDeclaration, Symbol**, 171 size_t count) const 172 { 173 return count; 174 } 175 176 override elem* getOffset(VarDeclaration var, Type type, elem* offset) const 177 { 178 return offset; 179 } 180 } 181 182 extern(C++) final class Supported : ObjcGlue 183 { 184 extern (D) this() 185 { 186 Segments.initialize(); 187 Symbols.initialize(); 188 } 189 190 override void reset() 191 { 192 Segments.reset(); 193 Symbols.reset(); 194 } 195 196 override void setupMethodSelector(FuncDeclaration fd, elem** esel) 197 { 198 if (fd && fd.objc.selector && !*esel) 199 { 200 *esel = el_var(Symbols.getMethVarRef(fd.objc.selector.toString())); 201 } 202 } 203 204 override ElemResult setupMethodCall(FuncDeclaration fd, TypeFunction tf, 205 bool directcall, elem* ec, elem* ehidden, elem* ethis) 206 { 207 import dmd.e2ir : addressElem; 208 209 if (directcall) // super call 210 { 211 ElemResult result; 212 // call through Objective-C runtime dispatch 213 result.ec = el_var(Symbols.getMsgSendSuper(ehidden !is null)); 214 215 // need to change this pointer to a pointer to an two-word 216 // objc_super struct of the form { this ptr, class ptr }. 217 auto cd = fd.isThis.isClassDeclaration; 218 assert(cd, "call to objc_msgSendSuper with no class declaration"); 219 220 // faking objc_super type as delegate 221 auto classRef = el_var(Symbols.getClassReference(cd)); 222 auto super_ = el_pair(TYdelegate, ethis, classRef); 223 224 result.ethis = addressElem(super_, tf); 225 226 return result; 227 } 228 229 else 230 { 231 // make objc-style "virtual" call using dispatch function 232 assert(ethis); 233 Type tret = tf.next; 234 235 ElemResult result = { 236 ec: el_var(Symbols.getMsgSend(tret, ehidden !is null)), 237 ethis: ethis 238 }; 239 240 return result; 241 } 242 } 243 244 override void setupEp(elem* esel, elem** ep, int leftToRight) 245 { 246 if (esel) 247 { 248 // using objc-style "virtual" call 249 // add hidden argument (second to 'this') for selector used by dispatch function 250 if (leftToRight) 251 *ep = el_param(esel, *ep); 252 else 253 *ep = el_param(*ep, esel); 254 } 255 } 256 257 override void generateModuleInfo(Module module_) 258 { 259 ClassDeclarations classes; 260 ClassDeclarations categories; 261 262 module_.members.foreachDsymbol(m => m.addObjcSymbols(&classes, &categories)); 263 264 if (classes.length || categories.length || Symbols.hasSymbols) 265 Symbols.getModuleInfo(classes, categories); 266 } 267 268 override elem* toElem(ObjcClassReferenceExp e) const 269 { 270 return el_var(Symbols.getClassReference(e.classDeclaration)); 271 } 272 273 override void toObjFile(ClassDeclaration classDeclaration) const 274 in 275 { 276 assert(classDeclaration !is null); 277 assert(classDeclaration.classKind == ClassKind.objc); 278 } 279 do 280 { 281 if (!classDeclaration.objc.isMeta) 282 ObjcClassDeclaration(classDeclaration, false).toObjFile(); 283 } 284 285 override size_t addSelectorParameterSymbol(FuncDeclaration fd, 286 Symbol** params, size_t count) const 287 in 288 { 289 assert(fd); 290 } 291 do 292 { 293 if (!fd.objc.selector) 294 return count; 295 296 assert(fd.objc.selectorParameter); 297 auto selectorSymbol = fd.objc.selectorParameter.toSymbol(); 298 memmove(params + 1, params, count * params[0].sizeof); 299 params[0] = selectorSymbol; 300 301 return count + 1; 302 } 303 304 override elem* getOffset(VarDeclaration var, Type type, elem* offset) const 305 { 306 auto typeClass = type.isTypeClass; 307 308 if (!typeClass || typeClass.sym.classKind != ClassKind.objc) 309 return offset; 310 311 return el_var(ObjcClassDeclaration(typeClass.sym, false).getIVarOffset(var)); 312 } 313 } 314 315 struct Segments 316 { 317 enum Id 318 { 319 classlist, 320 classname, 321 classrefs, 322 const_, 323 objcData, 324 imageinfo, 325 ivar, 326 methname, 327 methtype, 328 selrefs, 329 protolist, 330 data 331 } 332 333 private 334 { 335 __gshared int[Id] segments; 336 __gshared Segments[Id] segmentData; 337 338 immutable(char*) sectionName; 339 immutable(char*) segmentName; 340 immutable int flags; 341 immutable int alignment; 342 343 this(typeof(this.tupleof) tuple) @safe 344 { 345 this.tupleof = tuple; 346 } 347 348 static void initialize() 349 { 350 segmentData = [ 351 Id.classlist: Segments("__objc_classlist", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 3), 352 Id.classname: Segments("__objc_classname", "__TEXT", S_CSTRING_LITERALS, 0), 353 Id.classrefs: Segments("__objc_classrefs", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 3), 354 Id.const_: Segments("__objc_const", "__DATA", S_REGULAR, 3), 355 Id.objcData: Segments("__objc_data", "__DATA", S_REGULAR, 3), 356 Id.imageinfo: Segments("__objc_imageinfo", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 0), 357 Id.ivar: Segments("__objc_ivar", "__DATA", S_REGULAR, 3), 358 Id.methname: Segments("__objc_methname", "__TEXT", S_CSTRING_LITERALS, 0), 359 Id.methtype: Segments("__objc_methtype", "__TEXT", S_CSTRING_LITERALS, 0), 360 Id.selrefs: Segments("__objc_selrefs", "__DATA", S_LITERAL_POINTERS | S_ATTR_NO_DEAD_STRIP, 3), 361 Id.protolist: Segments("__objc_protolist", "__DATA", S_COALESCED | S_ATTR_NO_DEAD_STRIP, 3), 362 Id.data: Segments("__data", "__DATA", S_REGULAR, 3), 363 ]; 364 } 365 } 366 367 /// Resets the segments. 368 static void reset() 369 { 370 clearCache(); 371 } 372 373 // Clears any caches. 374 private static void clearCache() 375 { 376 segments.clear; 377 } 378 379 static int opIndex(Id id) 380 { 381 if (auto segment = id in segments) 382 return *segment; 383 384 const seg = segmentData[id]; 385 386 version (OSX) 387 { 388 return segments[id] = Obj.getsegment( 389 seg.sectionName, 390 seg.segmentName, 391 seg.alignment, 392 seg.flags 393 ); 394 } 395 396 else 397 { 398 // This should never happen. If the platform is not OSX an error 399 // should have occurred sooner which should have prevented the 400 // code from getting here. 401 assert(0); 402 } 403 } 404 } 405 406 struct Symbols 407 { 408 static: 409 410 private __gshared 411 { 412 alias SymbolCache = StringTable!(Symbol*)*; 413 414 bool hasSymbols_ = false; 415 416 Symbol* objc_msgSend = null; 417 Symbol* objc_msgSend_stret = null; 418 Symbol* objc_msgSend_fpret = null; 419 Symbol* objc_msgSend_fp2ret = null; 420 421 Symbol* objc_msgSendSuper = null; 422 Symbol* objc_msgSendSuper_stret = null; 423 424 Symbol* imageInfo = null; 425 Symbol* moduleInfo = null; 426 427 Symbol* emptyCache = null; 428 Symbol* emptyVTable = null; 429 430 // Cache for `_OBJC_METACLASS_$_`/`_OBJC_CLASS_$_` symbols. 431 SymbolCache classNameTable = null; 432 433 // Cache for `L_OBJC_CLASSLIST_REFERENCES_` symbols. 434 SymbolCache classReferenceTable = null; 435 436 // Cache for `__OBJC_PROTOCOL_$_` symbols. 437 SymbolCache protocolTable = null; 438 439 SymbolCache methVarNameTable = null; 440 SymbolCache methVarRefTable = null; 441 SymbolCache methVarTypeTable = null; 442 443 // Cache for instance variable offsets 444 SymbolCache ivarOffsetTable = null; 445 } 446 447 void initialize() 448 { 449 initializeStringTables(); 450 } 451 452 private void initializeStringTables() 453 { 454 alias This = typeof(this); 455 456 foreach (m ; __traits(allMembers, This)) 457 { 458 static if (is(typeof(__traits(getMember, This, m)) == SymbolCache)) 459 { 460 __traits(getMember, This, m) = new StringTable!(Symbol*)(); 461 __traits(getMember, This, m)._init(); 462 } 463 } 464 } 465 466 /// Resets the symbols. 467 void reset() 468 { 469 clearCache(); 470 resetSymbolCache(); 471 } 472 473 // Clears any caches. 474 private void clearCache() 475 { 476 alias This = typeof(this); 477 478 foreach (m ; __traits(allMembers, This)) 479 { 480 static if (is(typeof(__traits(getMember, This, m)) == Symbol*)) 481 __traits(getMember, This, m) = null; 482 } 483 } 484 485 // Resets the symbol caches. 486 private void resetSymbolCache() 487 { 488 alias This = typeof(this); 489 490 foreach (m ; __traits(allMembers, This)) 491 { 492 static if (is(typeof(__traits(getMember, This, m)) == SymbolCache)) 493 __traits(getMember, This, m).reset(); 494 } 495 } 496 497 bool hasSymbols() 498 { 499 if (hasSymbols_) 500 return true; 501 502 alias This = typeof(this); 503 504 foreach (m ; __traits(allMembers, This)) 505 { 506 static if (is(typeof(__traits(getMember, This, m)) == Symbol*)) 507 { 508 if (__traits(getMember, This, m) !is null) 509 return true; 510 } 511 } 512 513 return false; 514 } 515 516 /** 517 * Convenience wrapper around `dmd.backend.global.symbol_name`. 518 * 519 * Allows to pass the name of the symbol as a D string. 520 */ 521 Symbol* symbolName(const(char)[] name, SC sclass, type* t) @safe 522 { 523 return symbol_name(name, sclass, t); 524 } 525 526 /** 527 * Gets a global symbol. 528 * 529 * Params: 530 * name = the name of the symbol 531 * t = the type of the symbol 532 * 533 * Returns: the symbol 534 */ 535 Symbol* getGlobal(const(char)[] name, type* t = type_fake(TYnptr)) @safe 536 { 537 return symbolName(name, SC.global, t); 538 } 539 540 /** 541 * Gets a static symbol. 542 * 543 * Params: 544 * name = the name of the symbol 545 * t = the type of the symbol 546 * 547 * Returns: the symbol 548 */ 549 Symbol* getStatic(const(char)[] name, type* t = type_fake(TYnptr)) @safe 550 { 551 return symbolName(name, SC.static_, t); 552 } 553 554 Symbol* getCString(const(char)[] str, const(char)[] symbolName, Segments.Id segment) 555 { 556 hasSymbols_ = true; 557 558 // create data 559 auto dtb = DtBuilder(0); 560 dtb.nbytes(cast(uint) (str.length + 1), str.toStringz()); 561 562 // find segment 563 auto seg = Segments[segment]; 564 565 // create symbol 566 auto s = getStatic(symbolName, type_allocn(TYarray, tstypes[TYchar])); 567 s.Sdt = dtb.finish(); 568 s.Sseg = seg; 569 return s; 570 } 571 572 Symbol* getMethVarName(const(char)[] name) 573 { 574 return cache(name, methVarNameTable, { 575 __gshared size_t classNameCount = 0; 576 char[42] buffer; 577 const symbolName = format(buffer, "L_OBJC_METH_VAR_NAME_%lu", classNameCount++); 578 579 return getCString(name, symbolName, Segments.Id.methname); 580 }); 581 } 582 583 Symbol* getMethVarName(Identifier ident) 584 { 585 return getMethVarName(ident.toString()); 586 } 587 588 Symbol* getMsgSend(Type returnType, bool hasHiddenArgument) 589 { 590 if (hasHiddenArgument) 591 return setMsgSendSymbol!("_objc_msgSend_stret")(TYhfunc); 592 // not sure if DMD can handle this 593 else if (returnType.ty == Tcomplex80) 594 return setMsgSendSymbol!("_objc_msgSend_fp2ret"); 595 else if (returnType.ty == Tfloat80) 596 return setMsgSendSymbol!("_objc_msgSend_fpret"); 597 else 598 return setMsgSendSymbol!("_objc_msgSend"); 599 600 assert(0); 601 } 602 603 Symbol* getMsgSendSuper(bool hasHiddenArgument) 604 { 605 if (hasHiddenArgument) 606 return setMsgSendSymbol!("_objc_msgSendSuper_stret")(TYhfunc); 607 else 608 return setMsgSendSymbol!("_objc_msgSendSuper")(TYnfunc); 609 } 610 611 Symbol* getImageInfo() 612 { 613 if (imageInfo) 614 return imageInfo; 615 616 auto dtb = DtBuilder(0); 617 dtb.dword(0); // version 618 dtb.dword(64); // flags 619 620 imageInfo = symbol_name("L_OBJC_IMAGE_INFO", SC.static_, type_allocn(TYarray, tstypes[TYchar])); 621 imageInfo.Sdt = dtb.finish(); 622 imageInfo.Sseg = Segments[Segments.Id.imageinfo]; 623 outdata(imageInfo); 624 625 return imageInfo; 626 } 627 628 Symbol* getModuleInfo(/*const*/ ref ClassDeclarations classes, 629 /*const*/ ref ClassDeclarations categories) 630 { 631 assert(!moduleInfo); // only allow once per object file 632 633 auto dtb = DtBuilder(0); 634 635 foreach (c; classes) 636 dtb.xoff(getClassName(c), 0); 637 638 foreach (c; categories) 639 dtb.xoff(getClassName(c), 0); 640 641 Symbol* symbol = symbol_name("L_OBJC_LABEL_CLASS_$", SC.static_, type_allocn(TYarray, tstypes[TYchar])); 642 symbol.Sdt = dtb.finish(); 643 symbol.Sseg = Segments[Segments.Id.classlist]; 644 outdata(symbol); 645 646 getImageInfo(); // make sure we also generate image info 647 648 return moduleInfo; 649 } 650 651 /** 652 * Returns: the `_OBJC_METACLASS_$_`/`_OBJC_CLASS_$_` symbol for the given 653 * class declaration. 654 */ 655 Symbol* getClassName(ObjcClassDeclaration objcClass) 656 { 657 hasSymbols_ = true; 658 659 const prefix = objcClass.isMeta ? "_OBJC_METACLASS_$_" : "_OBJC_CLASS_$_"; 660 auto name = prefix ~ objcClass.classDeclaration.objc.identifier.toString(); 661 662 return cache(name, classNameTable, () => getGlobal(name)); 663 } 664 665 /// ditto 666 Symbol* getClassName(ClassDeclaration classDeclaration, bool isMeta = false) 667 in 668 { 669 assert(classDeclaration !is null); 670 } 671 do 672 { 673 return getClassName(ObjcClassDeclaration(classDeclaration, isMeta)); 674 } 675 676 /* 677 * Returns: the `L_OBJC_CLASSLIST_REFERENCES_$_` symbol for the given class 678 * declaration. 679 */ 680 Symbol* getClassReference(ClassDeclaration classDeclaration) 681 { 682 hasSymbols_ = true; 683 684 auto name = classDeclaration.objc.identifier.toString(); 685 686 return cache(name, classReferenceTable, { 687 auto dtb = DtBuilder(0); 688 auto className = getClassName(classDeclaration); 689 dtb.xoff(className, 0, TYnptr); 690 691 auto segment = Segments[Segments.Id.classrefs]; 692 693 __gshared size_t classReferenceCount = 0; 694 695 char[42] nameString; 696 auto result = format(nameString, "L_OBJC_CLASSLIST_REFERENCES_$_%lu", classReferenceCount++); 697 auto symbol = getStatic(result); 698 symbol.Sdt = dtb.finish(); 699 symbol.Sseg = segment; 700 outdata(symbol); 701 702 return symbol; 703 }); 704 } 705 706 Symbol* getMethVarRef(const(char)[] name) 707 { 708 return cache(name, methVarRefTable, { 709 // create data 710 auto dtb = DtBuilder(0); 711 auto selector = getMethVarName(name); 712 dtb.xoff(selector, 0, TYnptr); 713 714 // find segment 715 auto seg = Segments[Segments.Id.selrefs]; 716 717 // create symbol 718 __gshared size_t selectorCount = 0; 719 char[42] nameString = void; 720 const len = snprintf(nameString.ptr, nameString.length, "L_OBJC_SELECTOR_REFERENCES_%llu", cast(ulong) selectorCount); 721 auto symbol = symbol_name(nameString[0 .. len], SC.static_, type_fake(TYnptr)); 722 723 symbol.Sdt = dtb.finish(); 724 symbol.Sseg = seg; 725 outdata(symbol); 726 727 ++selectorCount; 728 729 return symbol; 730 }); 731 } 732 733 Symbol* getMethVarRef(const Identifier ident) 734 { 735 return getMethVarRef(ident.toString()); 736 } 737 738 /** 739 * Returns the Objective-C type encoding for the given type. 740 * 741 * The available type encodings are documented by Apple, available at 742 * $(LINK2 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100, Type Encoding). 743 * The type encodings can also be obtained by running an Objective-C 744 * compiler and using the `@encode()` compiler directive. 745 * 746 * Params: 747 * type = the type to return the type encoding for 748 * 749 * Returns: a string containing the type encoding 750 */ 751 string getTypeEncoding(Type type) @safe 752 in 753 { 754 assert(type !is null); 755 } 756 do 757 { 758 enum assertMessage = "imaginary types are not supported by Objective-C"; 759 760 with (TY) switch (type.ty) 761 { 762 case Tvoid: return "v"; 763 case Tbool: return "B"; 764 case Tint8: return "c"; 765 case Tuns8: return "C"; 766 case Tchar: return "C"; 767 case Tint16: return "s"; 768 case Tuns16: return "S"; 769 case Twchar: return "S"; 770 case Tint32: return "i"; 771 case Tuns32: return "I"; 772 case Tdchar: return "I"; 773 case Tint64: return "q"; 774 case Tuns64: return "Q"; 775 case Tfloat32: return "f"; 776 case Tcomplex32: return "jf"; 777 case Tfloat64: return "d"; 778 case Tcomplex64: return "jd"; 779 case Tfloat80: return "D"; 780 case Tcomplex80: return "jD"; 781 case Timaginary32: assert(false, assertMessage); 782 case Timaginary64: assert(false, assertMessage); 783 case Timaginary80: assert(false, assertMessage); 784 default: return "?"; // unknown 785 // TODO: add "*" char*, "#" Class, "@" id, ":" SEL 786 // TODO: add "^"<type> indirection and "^^" double indirection 787 } 788 } 789 790 /** 791 * Returns: the `L_OBJC_METH_VAR_TYPE_` symbol containing the given 792 * type encoding. 793 */ 794 Symbol* getMethVarType(const(char)[] typeEncoding) 795 { 796 return cache(typeEncoding, methVarTypeTable, { 797 __gshared size_t count = 0; 798 char[42] nameString; 799 const symbolName = format(nameString, "L_OBJC_METH_VAR_TYPE_%lu", count++); 800 auto symbol = getCString(typeEncoding, symbolName, Segments.Id.methtype); 801 802 outdata(symbol); 803 804 return symbol; 805 }); 806 } 807 808 /// ditto 809 Symbol* getMethVarType(Type[] types ...) 810 { 811 string typeCode; 812 typeCode.reserve(types.length); 813 814 foreach (type; types) 815 typeCode ~= getTypeEncoding(type); 816 817 return getMethVarType(typeCode); 818 } 819 820 /// ditto 821 Symbol* getMethVarType(FuncDeclaration func) 822 { 823 Type[] types = [func.type.nextOf]; // return type first 824 825 if (func.parameters) 826 { 827 types.reserve(func.parameters.length); 828 829 foreach (e; *func.parameters) 830 types ~= e.type; 831 } 832 833 return getMethVarType(types); 834 } 835 836 /// Returns: the externally defined `__objc_empty_cache` symbol 837 Symbol* getEmptyCache() 838 { 839 return emptyCache = emptyCache ? emptyCache : getGlobal("__objc_empty_cache"); 840 } 841 842 /// Returns: the externally defined `__objc_empty_vtable` symbol 843 Symbol* getEmptyVTable() 844 { 845 return emptyVTable = emptyVTable ? emptyVTable : getGlobal("__objc_empty_vtable"); 846 } 847 848 /// Returns: the `L_OBJC_CLASS_NAME_` symbol for a class with the given name 849 Symbol* getClassNameRo(const(char)[] name) 850 { 851 return cache(name, classNameTable, { 852 __gshared size_t count = 0; 853 char[42] nameString; 854 const symbolName = format(nameString, "L_OBJC_CLASS_NAME_%lu", count++); 855 856 return getCString(name, symbolName, Segments.Id.classname); 857 }); 858 } 859 860 /// ditto 861 Symbol* getClassNameRo(const Identifier ident) 862 { 863 return getClassNameRo(ident.toString()); 864 } 865 866 Symbol* getIVarOffset(ClassDeclaration cd, VarDeclaration var, bool outputSymbol) 867 { 868 hasSymbols_ = true; 869 870 const className = cd.objc.identifier.toString; 871 const varName = var.ident.toString; 872 const name = "_OBJC_IVAR_$_" ~ className ~ '.' ~ varName; 873 874 auto stringValue = ivarOffsetTable.update(name); 875 auto symbol = stringValue.value; 876 877 if (!symbol) 878 { 879 symbol = getGlobal(name); 880 symbol.Sfl |= FLextern; 881 stringValue.value = symbol; 882 } 883 884 if (!outputSymbol) 885 return symbol; 886 887 auto dtb = DtBuilder(0); 888 dtb.size(var.offset); 889 890 symbol.Sdt = dtb.finish(); 891 symbol.Sseg = Segments[Segments.Id.ivar]; 892 symbol.Sfl &= ~FLextern; 893 894 outdata(symbol); 895 896 return symbol; 897 } 898 899 Symbol* getProtocolSymbol(InterfaceDeclaration id) 900 in 901 { 902 assert(!id.objc.isMeta); 903 } 904 do 905 { 906 const name = id.objc.identifier.toString(); 907 return cache(name, protocolTable, () => ProtocolDeclaration(id).toObjFile()); 908 } 909 910 private Symbol* setMsgSendSymbol(string name)(tym_t ty = TYnfunc) 911 { 912 alias This = typeof(this); 913 enum fieldName = name[1 .. $]; 914 915 if (!__traits(getMember, This, fieldName)) 916 __traits(getMember, This, fieldName) = getGlobal(name, type_fake(ty)); 917 918 return __traits(getMember, This, fieldName); 919 } 920 921 /** 922 * Caches the symbol returned by `block` using the given name. 923 * 924 * If the symbol is already in the cache, the symbol will be returned 925 * immediately and `block` will not be called. 926 * 927 * Params: 928 * name = the name to cache the symbol under 929 * symbolCache = the cache storage to use for this symbol 930 * block = invoked when the symbol is not in the cache. The return value 931 * will be put into the cache 932 * 933 * Returns: the cached symbol 934 */ 935 private Symbol* cache(const(char)[] name, SymbolCache symbolCache, 936 scope Symbol* delegate() block) 937 { 938 hasSymbols_ = true; 939 940 auto stringValue = symbolCache.update(name); 941 942 if (stringValue.value) 943 return stringValue.value; 944 945 return stringValue.value = block(); 946 } 947 } 948 949 private: 950 951 /** 952 * Functionality for outputting symbols for a specific Objective-C class 953 * declaration. 954 */ 955 struct ObjcClassDeclaration 956 { 957 /// Indicates what kind of class this is. 958 private enum Flags 959 { 960 /// Regular class. 961 regular = 0x00000, 962 963 /// Meta class. 964 meta = 0x00001, 965 966 /// Root class. A class without any base class. 967 root = 0x00002 968 } 969 970 /// The class declaration 971 ClassDeclaration classDeclaration; 972 973 /// `true` if this class is a metaclass. 974 bool isMeta; 975 976 this(ClassDeclaration classDeclaration, bool isMeta) @safe 977 in 978 { 979 assert(classDeclaration !is null); 980 } 981 do 982 { 983 this.classDeclaration = classDeclaration; 984 this.isMeta = isMeta; 985 } 986 987 /** 988 * Outputs the class declaration to the object file. 989 * 990 * Returns: the exported symbol, that is, `_OBJC_METACLASS_$_` or 991 * `_OBJC_CLASS_$_` 992 */ 993 Symbol* toObjFile() 994 { 995 if (classDeclaration.objc.isExtern) 996 return null; // only a declaration for an externally-defined class 997 998 auto dtb = DtBuilder(0); 999 toDt(dtb); 1000 1001 auto symbol = Symbols.getClassName(this); 1002 symbol.Sdt = dtb.finish(); 1003 symbol.Sseg = Segments[Segments.Id.objcData]; 1004 outdata(symbol); 1005 1006 return symbol; 1007 } 1008 1009 private: 1010 1011 /** 1012 * Outputs the class declaration to the object file. 1013 * 1014 * Params: 1015 * dtb = the `DtBuilder` to output the class declaration to 1016 */ 1017 void toDt(ref DtBuilder dtb) 1018 { 1019 auto baseClassSymbol = classDeclaration.baseClass ? 1020 Symbols.getClassName(classDeclaration.baseClass, isMeta) : null; 1021 1022 dtb.xoff(getMetaclass(), 0); // pointer to metaclass 1023 dtb.xoffOrNull(baseClassSymbol); // pointer to base class 1024 dtb.xoff(Symbols.getEmptyCache(), 0); 1025 dtb.xoff(Symbols.getEmptyVTable(), 0); 1026 dtb.xoff(getClassRo(), 0); 1027 } 1028 1029 /// Returns: the name of the metaclass of this class declaration 1030 Symbol* getMetaclass() 1031 { 1032 if (isMeta) 1033 { 1034 // metaclass: return root class's name 1035 // (will be replaced with metaclass reference at load) 1036 1037 auto metaclassDeclaration = classDeclaration; 1038 1039 while (metaclassDeclaration.baseClass) 1040 metaclassDeclaration = metaclassDeclaration.baseClass; 1041 1042 return Symbols.getClassName(metaclassDeclaration, true); 1043 } 1044 1045 else 1046 { 1047 // regular class: return metaclass with the same name 1048 return ObjcClassDeclaration(classDeclaration, true).toObjFile(); 1049 } 1050 } 1051 1052 /** 1053 * Returns: the `l_OBJC_CLASS_RO_$_`/`l_OBJC_METACLASS_RO_$_` symbol for 1054 * this class declaration 1055 */ 1056 Symbol* getClassRo() 1057 { 1058 auto dtb = DtBuilder(0); 1059 1060 dtb.dword(flags); 1061 dtb.dword(instanceStart); 1062 dtb.dword(instanceSize); 1063 dtb.dword(0); // reserved 1064 1065 dtb.size(0); // ivar layout 1066 dtb.xoff(Symbols.getClassNameRo(classDeclaration.ident), 0); // name of the class 1067 1068 dtb.xoffOrNull(getMethodList()); // instance method list 1069 dtb.xoffOrNull(getProtocolList()); // protocol list 1070 1071 if (isMeta) 1072 { 1073 dtb.size(0); // instance variable list 1074 dtb.size(0); // weak ivar layout 1075 dtb.size(0); // properties 1076 } 1077 1078 else 1079 { 1080 dtb.xoffOrNull(getIVarList()); // instance variable list 1081 dtb.size(0); // weak ivar layout 1082 dtb.xoffOrNull(getPropertyList()); // properties 1083 } 1084 1085 const prefix = isMeta ? "l_OBJC_METACLASS_RO_$_" : "l_OBJC_CLASS_RO_$_"; 1086 const symbolName = prefix ~ classDeclaration.objc.identifier.toString(); 1087 auto symbol = Symbols.getStatic(symbolName); 1088 1089 symbol.Sdt = dtb.finish(); 1090 symbol.Sseg = Segments[Segments.Id.const_]; 1091 outdata(symbol); 1092 1093 return symbol; 1094 } 1095 1096 /** 1097 * Returns method list for this class declaration. 1098 * 1099 * This is a list of all methods defined in this class declaration, i.e. 1100 * methods with a body. 1101 * 1102 * Returns: the symbol for the method list, `l_OBJC_$_CLASS_METHODS_` or 1103 * `l_OBJC_$_INSTANCE_METHODS_` 1104 */ 1105 Symbol* getMethodList() 1106 { 1107 auto methods = isMeta ? classDeclaration.objc.metaclass.objc.methodList : 1108 classDeclaration.objc.methodList; 1109 1110 auto methodsWithBody = methods.filter!(m => m.fbody); 1111 const methodCount = methodsWithBody.walkLength; 1112 1113 if (methodCount == 0) 1114 return null; 1115 1116 auto dtb = DtBuilder(0); 1117 1118 dtb.dword(24); // _objc_method.sizeof 1119 dtb.dword(cast(int) methodCount); // method count 1120 1121 foreach (func; methodsWithBody) 1122 { 1123 assert(func.objc.selector); 1124 1125 dtb.xoff(func.objc.selector.toNameSymbol(), 0); // method name 1126 dtb.xoff(Symbols.getMethVarType(func), 0); // method type string 1127 dtb.xoff(func.toSymbol(), 0); // function implementation 1128 } 1129 1130 const prefix = isMeta ? "l_OBJC_$_CLASS_METHODS_" : "l_OBJC_$_INSTANCE_METHODS_"; 1131 const symbolName = prefix ~ classDeclaration.objc.identifier.toString(); 1132 auto symbol = Symbols.getStatic(symbolName); 1133 1134 symbol.Sdt = dtb.finish(); 1135 symbol.Sseg = Segments[Segments.Id.const_]; 1136 1137 return symbol; 1138 } 1139 1140 Symbol* getProtocolList() 1141 { 1142 if (classDeclaration.interfaces.length == 0) 1143 return null; 1144 1145 auto dtb = DtBuilder(0); 1146 dtb.size(classDeclaration.interfaces.length); // count 1147 1148 auto protocolSymbols = classDeclaration 1149 .interfaces 1150 .map!(base => cast(InterfaceDeclaration) base.sym) 1151 .map!(Symbols.getProtocolSymbol); 1152 1153 foreach (symbol; protocolSymbols) 1154 dtb.xoff(symbol, 0); // pointer to protocol declaration 1155 1156 dtb.size(0); // null-terminate the list 1157 1158 enum prefix = "__OBJC_CLASS_PROTOCOLS_$_"; 1159 const symbolName = prefix ~ classDeclaration.objc.identifier.toString(); 1160 auto symbol = Symbols.getStatic(symbolName); 1161 symbol.Sseg = Segments[Segments.Id.const_]; 1162 symbol.Salignment = 3; 1163 symbol.Sdt = dtb.finish(); 1164 1165 return symbol; 1166 } 1167 1168 Symbol* getIVarList() 1169 { 1170 if (isMeta || classDeclaration.fields.length == 0) 1171 return null; 1172 1173 auto dtb = DtBuilder(0); 1174 1175 dtb.dword(32); // entsize, _ivar_t.sizeof 1176 dtb.dword(cast(int) classDeclaration.fields.length); // ivar count 1177 1178 foreach (field; classDeclaration.fields) 1179 { 1180 auto var = field.isVarDeclaration; 1181 assert(var); 1182 assert((var.storage_class & STC.static_) == 0); 1183 1184 dtb.xoff(Symbols.getIVarOffset(classDeclaration, var, true), 0); // pointer to ivar offset 1185 dtb.xoff(Symbols.getMethVarName(var.ident), 0); // name 1186 dtb.xoff(Symbols.getMethVarType(var.type), 0); // type string 1187 dtb.dword(var.alignment.isDefault() ? -1 : var.alignment.get()); 1188 dtb.dword(cast(int) var.size(var.loc)); 1189 } 1190 1191 enum prefix = "l_OBJC_$_INSTANCE_VARIABLES_"; 1192 const symbolName = prefix ~ classDeclaration.objc.identifier.toString(); 1193 auto symbol = Symbols.getStatic(symbolName); 1194 1195 symbol.Sdt = dtb.finish(); 1196 symbol.Sseg = Segments[Segments.Id.const_]; 1197 1198 return symbol; 1199 } 1200 1201 Symbol* getPropertyList() @safe 1202 { 1203 // properties are not supported yet 1204 return null; 1205 } 1206 1207 Symbol* getIVarOffset(VarDeclaration var) 1208 { 1209 if (var.toParent() is classDeclaration) 1210 return Symbols.getIVarOffset(classDeclaration, var, false); 1211 1212 else if (classDeclaration.baseClass) 1213 return ObjcClassDeclaration(classDeclaration.baseClass, false) 1214 .getIVarOffset(var); 1215 1216 else 1217 assert(false, "Trying to get the base class of a root class"); 1218 } 1219 1220 /** 1221 * Returns the flags for this class declaration. 1222 * 1223 * That is, if this is a regular class, a metaclass and/or a root class. 1224 * 1225 * Returns: the flags 1226 */ 1227 uint flags() const @safe 1228 { 1229 uint flags = isMeta ? Flags.meta : Flags.regular; 1230 1231 if (classDeclaration.objc.isRootClass) 1232 flags |= Flags.root; 1233 1234 return flags; 1235 } 1236 1237 /** 1238 * Returns the offset of where an instance of this class starts. 1239 * 1240 * For a metaclass this is always `40`. For a class with no instance 1241 * variables this is the size of the class declaration. For a class with 1242 * instance variables it's the offset of the first instance variable. 1243 * 1244 * Returns: the instance start 1245 */ 1246 int instanceStart() 1247 { 1248 if (isMeta) 1249 return 40; 1250 1251 const start = cast(uint) classDeclaration.size(classDeclaration.loc); 1252 1253 if (!classDeclaration.members || classDeclaration.members.length == 0) 1254 return start; 1255 1256 foreach (member; *classDeclaration.members) 1257 { 1258 auto var = member.isVarDeclaration; 1259 1260 if (var && var.isField) 1261 return var.offset; 1262 } 1263 1264 return start; 1265 } 1266 1267 /// Returns: the size of an instance of this class 1268 int instanceSize() 1269 { 1270 return isMeta ? 40 : cast(int) classDeclaration.size(classDeclaration.loc); 1271 } 1272 } 1273 1274 /** 1275 * Functionality for outputting symbols for a specific Objective-C protocol 1276 * declaration. 1277 */ 1278 struct ProtocolDeclaration 1279 { 1280 /// The interface declaration 1281 private InterfaceDeclaration interfaceDeclaration; 1282 1283 this(InterfaceDeclaration interfaceDeclaration) @safe 1284 in 1285 { 1286 assert(interfaceDeclaration !is null); 1287 } 1288 do 1289 { 1290 this.interfaceDeclaration = interfaceDeclaration; 1291 } 1292 1293 /** 1294 * Outputs the protocol declaration to the object file. 1295 * 1296 * Returns: the exported symbol, that is, `__OBJC_PROTOCOL_$_` 1297 */ 1298 Symbol* toObjFile() 1299 { 1300 const name = interfaceDeclaration.objc.identifier.toString(); 1301 1302 auto type = type_fake(TYnptr); 1303 type_setty(&type, type.Tty | mTYweakLinkage); 1304 1305 void createLabel(Symbol* protocol) 1306 { 1307 enum prefix = "__OBJC_LABEL_PROTOCOL_$_"; 1308 auto symbolName = prefix ~ name; 1309 1310 auto symbol = Symbols.getGlobal(symbolName, type); 1311 symbol.Sseg = Segments[Segments.Id.protolist]; 1312 symbol.Sclass = SC.comdat; 1313 symbol.Sflags |= SFLhidden; 1314 symbol.Salignment = 3; 1315 1316 auto dtb = DtBuilder(0); 1317 dtb.xoff(protocol, 0); 1318 symbol.Sdt = dtb.finish(); 1319 outdata(symbol); 1320 } 1321 1322 enum prefix = "__OBJC_PROTOCOL_$_"; 1323 auto symbolName = prefix ~ name; 1324 1325 auto symbol = Symbols.getGlobal(symbolName, type); 1326 symbol.Sseg = Segments[Segments.Id.data]; 1327 symbol.Sclass = SC.comdat; 1328 symbol.Sflags |= SFLhidden; 1329 symbol.Salignment = 3; 1330 1331 auto dtb = DtBuilder(0); 1332 toDt(dtb); 1333 symbol.Sdt = dtb.finish(); 1334 outdata(symbol); 1335 1336 createLabel(symbol); 1337 1338 return symbol; 1339 } 1340 1341 private: 1342 1343 /** 1344 * Outputs the protocols declaration to the object file. 1345 * 1346 * Params: 1347 * dtb = the `DtBuilder` to output the protocol declaration to 1348 */ 1349 void toDt(ref DtBuilder dtb) 1350 { 1351 dtb.size(0); // isa, always null 1352 dtb.xoff(Symbols.getClassNameRo(interfaceDeclaration.ident), 0); // name 1353 dtb.xoffOrNull(protocolList); // protocols 1354 1355 dtb.xoffOrNull(instanceMethodList); // instance methods 1356 dtb.xoffOrNull(classMethodList); // class methods 1357 dtb.xoffOrNull(optionalInstanceMethodList); // optional instance methods 1358 dtb.xoffOrNull(optionalClassMethodList); // optional class methods 1359 1360 dtb.size(0); // instance properties 1361 dtb.dword(96); // the size of _protocol_t, always 96 1362 dtb.dword(0); // flags, seems to always be 0 1363 1364 dtb.xoffOrNull(getMethodTypes); // extended method types 1365 1366 dtb.size(0); // demangled name. Used by Swift, unused by Objective-C 1367 dtb.size(0); // class properties 1368 } 1369 1370 /** 1371 * Returns instance method list for this protocol declaration. 1372 * 1373 * This is a list of all instance methods declared in this protocol 1374 * declaration. 1375 * 1376 * Returns: the symbol for the method list, `__OBJC_$_PROTOCOL_INSTANCE_METHODS_` 1377 */ 1378 Symbol* instanceMethodList() 1379 { 1380 enum symbolNamePrefix = "__OBJC_$_PROTOCOL_INSTANCE_METHODS_"; 1381 auto methods = interfaceDeclaration 1382 .objc 1383 .methodList 1384 .filter!(m => !m.objc.isOptional); 1385 1386 return methodList(symbolNamePrefix, methods); 1387 } 1388 1389 /** 1390 * Returns class method list for this protocol declaration. 1391 * 1392 * This is a list of all class methods declared in this protocol 1393 * declaration. 1394 * 1395 * Returns: the symbol for the method list, `__OBJC_$_PROTOCOL_CLASS_METHODS_` 1396 */ 1397 Symbol* classMethodList() 1398 { 1399 enum symbolNamePrefix = "__OBJC_$_PROTOCOL_CLASS_METHODS_"; 1400 auto methods = interfaceDeclaration 1401 .objc 1402 .metaclass 1403 .objc 1404 .methodList 1405 .filter!(m => !m.objc.isOptional); 1406 1407 return methodList(symbolNamePrefix, methods); 1408 } 1409 1410 /** 1411 * Returns optional instance method list for this protocol declaration. 1412 * 1413 * This is a list of all optional instance methods declared in this protocol 1414 * declaration. 1415 * 1416 * Returns: the symbol for the method list, `l_OBJC_$_PROTOCOL_INSTANCE_METHODS_` 1417 */ 1418 Symbol* optionalInstanceMethodList() 1419 { 1420 enum symbolNamePrefix = "l_OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_"; 1421 1422 auto methods = interfaceDeclaration 1423 .objc 1424 .methodList 1425 .filter!(m => m.objc.isOptional); 1426 1427 return methodList(symbolNamePrefix, methods); 1428 } 1429 1430 /** 1431 * Returns optional class method list for this protocol declaration. 1432 * 1433 * This is a list of all optional class methods declared in this protocol 1434 * declaration. 1435 * 1436 * Returns: the symbol for the method list, `l_OBJC_$_PROTOCOL_INSTANCE_METHODS_` 1437 */ 1438 Symbol* optionalClassMethodList() 1439 { 1440 enum symbolNamePrefix = "l_OBJC_$_PROTOCOL_CLASS_METHODS_OPT_"; 1441 auto methods = interfaceDeclaration 1442 .objc 1443 .metaclass 1444 .objc 1445 .methodList 1446 .filter!(m => m.objc.isOptional); 1447 1448 return methodList(symbolNamePrefix, methods); 1449 } 1450 1451 /** 1452 * Returns a method list for this protocol declaration. 1453 * 1454 * Returns: the symbol for the method list 1455 */ 1456 Symbol* methodList(Range)(string symbolNamePrefix, Range methods) 1457 if (isInputRange!Range && is(ElementType!Range == FuncDeclaration)) 1458 { 1459 const methodCount = methods.walkLength; 1460 1461 if (methodCount == 0) 1462 return null; 1463 1464 auto dtb = DtBuilder(0); 1465 1466 dtb.dword(24); // _objc_method.sizeof 1467 dtb.dword(cast(int) methodCount); // method count 1468 1469 foreach (func; methods) 1470 { 1471 dtb.xoff(func.objc.selector.toNameSymbol(), 0); // method name 1472 dtb.xoff(Symbols.getMethVarType(func), 0); // method type string 1473 dtb.size(0); // NULL, protocol methods have no implementation 1474 } 1475 1476 const symbolName = symbolNamePrefix ~ interfaceDeclaration.objc.identifier.toString(); 1477 auto symbol = Symbols.getStatic(symbolName); 1478 1479 symbol.Sdt = dtb.finish(); 1480 symbol.Sseg = Segments[Segments.Id.const_]; 1481 symbol.Salignment = 3; 1482 1483 return symbol; 1484 } 1485 1486 /** 1487 * Returns a list of type encodings for this protocol declaration. 1488 * 1489 * This is a list of all the type encodings for all methods declared in this 1490 * protocol declaration. 1491 * 1492 * Returns: the symbol for the type encodings, `__OBJC_$_PROTOCOL_METHOD_TYPES_` 1493 */ 1494 Symbol* getMethodTypes() 1495 { 1496 if (interfaceDeclaration.objc.methodList.length == 0) 1497 return null; 1498 1499 auto dtb = DtBuilder(0); 1500 1501 auto varTypeSymbols = interfaceDeclaration 1502 .objc 1503 .methodList 1504 .map!(Symbols.getMethVarType); 1505 1506 foreach (symbol; varTypeSymbols) 1507 dtb.xoff(symbol, 0); // method type string 1508 1509 enum prefix = "__OBJC_$_PROTOCOL_METHOD_TYPES_"; 1510 const symbolName = prefix ~ interfaceDeclaration.objc.identifier.toString(); 1511 auto symbol = Symbols.getStatic(symbolName); 1512 1513 symbol.Sdt = dtb.finish(); 1514 symbol.Sseg = Segments[Segments.Id.const_]; 1515 symbol.Salignment = 3; 1516 1517 outdata(symbol); 1518 1519 return symbol; 1520 } 1521 1522 /// Returns: the symbol for the protocol references, `__OBJC_$_PROTOCOL_REFS_` 1523 Symbol* protocolList() 1524 { 1525 auto interfaces = interfaceDeclaration.interfaces; 1526 1527 if (interfaces.length == 0) 1528 return null; 1529 1530 auto dtb = DtBuilder(0); 1531 1532 dtb.size(interfaces.length); // number of protocols in the list 1533 1534 auto symbols = interfaces 1535 .map!(i => cast(InterfaceDeclaration) i.sym) 1536 .map!(Symbols.getProtocolSymbol); 1537 1538 foreach (s; symbols) 1539 dtb.xoff(s, 0); // pointer to protocol declaration 1540 1541 dtb.size(0); // null-terminate the list 1542 1543 const prefix = "__OBJC_$_PROTOCOL_REFS_"; 1544 const symbolName = prefix ~ interfaceDeclaration.objc.identifier.toString(); 1545 auto symbol = Symbols.getStatic(symbolName); 1546 1547 symbol.Sdt = dtb.finish(); 1548 symbol.Sseg = Segments[Segments.Id.const_]; 1549 symbol.Salignment = 3; 1550 1551 outdata(symbol); 1552 1553 return symbol; 1554 } 1555 } 1556 1557 private: 1558 1559 /* 1560 * Formats the given arguments into the given buffer. 1561 * 1562 * Convenience wrapper around `snprintf`. 1563 * 1564 * Params: 1565 * bufLength = length of the buffer 1566 * buffer = the buffer where to store the result 1567 * format = the format string 1568 * args = the arguments to format 1569 * 1570 * Returns: the formatted result, a slice of the given buffer 1571 */ 1572 char[] format(size_t bufLength, Args...)(return ref char[bufLength] buffer, 1573 const(char)* format, const Args args) 1574 { 1575 auto length = snprintf(buffer.ptr, buffer.length, format, args); 1576 1577 assert(length >= 0, "An output error occurred"); 1578 assert(length < buffer.length, "Output was truncated"); 1579 1580 return buffer[0 .. length]; 1581 } 1582 1583 /// Returns: the symbol of the given selector 1584 Symbol* toNameSymbol(const ObjcSelector* selector) 1585 { 1586 return Symbols.getMethVarName(selector.toString()); 1587 } 1588 1589 /** 1590 * Adds a reference to the given `symbol` or null if the symbol is null. 1591 * 1592 * Params: 1593 * dtb = the dt builder to add the symbol to 1594 * symbol = the symbol to add 1595 */ 1596 void xoffOrNull(ref DtBuilder dtb, Symbol* symbol) @safe 1597 { 1598 if (symbol) 1599 dtb.xoff(symbol, 0); 1600 else 1601 dtb.size(0); 1602 } 1603 1604 /** 1605 * Converts the given D string to a null terminated C string. 1606 * 1607 * Asserts if `str` is longer than `maxLength`, with assertions enabled. With 1608 * assertions disabled it will truncate the result to `maxLength`. 1609 * 1610 * Params: 1611 * maxLength = the max length of `str` 1612 * str = the string to convert 1613 * buf = the buffer where to allocate the result. By default this will be 1614 * allocated in the caller scope using `alloca`. If the buffer is created 1615 * by the callee it needs to be able to fit at least `str.length + 1` bytes 1616 * 1617 * Returns: the given string converted to a C string, a slice of `str` or the 1618 * given buffer `buffer` 1619 */ 1620 const(char)* toStringz(size_t maxLength = 4095)(in const(char)[] str, 1621 scope return void[] buffer = alloca(maxLength + 1)[0 .. maxLength + 1]) pure 1622 in 1623 { 1624 assert(maxLength >= str.length); 1625 } 1626 out(result) 1627 { 1628 assert(str.length == result.strlen); 1629 } 1630 do 1631 { 1632 if (str.length == 0) 1633 return "".ptr; 1634 1635 const maxLength = buffer.length - 1; 1636 const len = str.length > maxLength ? maxLength : str.length; 1637 auto buf = cast(char[]) buffer[0 .. len + 1]; 1638 buf[0 .. len] = str[0 .. len]; 1639 buf[len] = '\0'; 1640 1641 return cast(const(char)*) buf.ptr; 1642 }