1 /** 2 * Glue code for Objective-C interop. 3 * 4 * Copyright: Copyright (C) 2015-2020 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 http://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.dclass; 21 import dmd.declaration; 22 import dmd.dmodule; 23 import dmd.dsymbol; 24 import dmd.expression; 25 import dmd.func; 26 import dmd.glue; 27 import dmd.identifier; 28 import dmd.mtype; 29 import dmd.objc; 30 import dmd.target; 31 32 import dmd.root.stringtable; 33 import dmd.root.array; 34 35 import dmd.backend.dt; 36 import dmd.backend.cc; 37 import dmd.backend.cdef; 38 import dmd.backend.el; 39 import dmd.backend.global; 40 import dmd.backend.oper; 41 import dmd.backend.outbuf; 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 data, 324 imageinfo, 325 ivar, 326 methname, 327 methtype, 328 selrefs 329 } 330 331 private 332 { 333 __gshared int[Id] segments; 334 __gshared Segments[Id] segmentData; 335 336 immutable(char*) sectionName; 337 immutable(char*) segmentName; 338 immutable int flags; 339 immutable int alignment; 340 341 this(typeof(this.tupleof) tuple) 342 { 343 this.tupleof = tuple; 344 } 345 346 static void initialize() 347 { 348 segmentData = [ 349 Id.classlist: Segments("__objc_classlist", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 3), 350 Id.classname: Segments("__objc_classname", "__TEXT", S_CSTRING_LITERALS, 0), 351 Id.classrefs: Segments("__objc_classrefs", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 3), 352 Id.const_: Segments("__objc_const", "__DATA", S_REGULAR, 3), 353 Id.data: Segments("__objc_data", "__DATA", S_REGULAR, 3), 354 Id.imageinfo: Segments("__objc_imageinfo", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 0), 355 Id.ivar: Segments("__objc_ivar", "__DATA", S_REGULAR, 3), 356 Id.methname: Segments("__objc_methname", "__TEXT", S_CSTRING_LITERALS, 0), 357 Id.methtype: Segments("__objc_methtype", "__TEXT", S_CSTRING_LITERALS, 0), 358 Id.selrefs: Segments("__objc_selrefs", "__DATA", S_LITERAL_POINTERS | S_ATTR_NO_DEAD_STRIP, 3), 359 ]; 360 } 361 } 362 363 /// Resets the segments. 364 static void reset() 365 { 366 clearCache(); 367 } 368 369 // Clears any caches. 370 private static void clearCache() 371 { 372 segments.clear; 373 } 374 375 static int opIndex(Id id) 376 { 377 if (auto segment = id in segments) 378 return *segment; 379 380 const seg = segmentData[id]; 381 382 version (OSX) 383 { 384 return segments[id] = Obj.getsegment( 385 seg.sectionName, 386 seg.segmentName, 387 seg.alignment, 388 seg.flags 389 ); 390 } 391 392 else 393 { 394 // This should never happen. If the platform is not OSX an error 395 // should have occurred sooner which should have prevented the 396 // code from getting here. 397 assert(0); 398 } 399 } 400 } 401 402 struct Symbols 403 { 404 static: 405 406 private __gshared 407 { 408 alias SymbolCache = StringTable!(Symbol*)*; 409 410 bool hasSymbols_ = false; 411 412 Symbol* objc_msgSend = null; 413 Symbol* objc_msgSend_stret = null; 414 Symbol* objc_msgSend_fpret = null; 415 Symbol* objc_msgSend_fp2ret = null; 416 417 Symbol* objc_msgSendSuper = null; 418 Symbol* objc_msgSendSuper_stret = null; 419 420 Symbol* imageInfo = null; 421 Symbol* moduleInfo = null; 422 423 Symbol* emptyCache = null; 424 Symbol* emptyVTable = null; 425 426 // Cache for `_OBJC_METACLASS_$_`/`_OBJC_CLASS_$_` symbols. 427 SymbolCache classNameTable = null; 428 429 // Cache for `L_OBJC_CLASSLIST_REFERENCES_` symbols. 430 SymbolCache classReferenceTable = null; 431 432 SymbolCache methVarNameTable = null; 433 SymbolCache methVarRefTable = null; 434 SymbolCache methVarTypeTable = null; 435 436 // Cache for instance variable offsets 437 SymbolCache ivarOffsetTable = null; 438 } 439 440 void initialize() 441 { 442 initializeStringTables(); 443 } 444 445 private void initializeStringTables() 446 { 447 alias This = typeof(this); 448 449 foreach (m ; __traits(allMembers, This)) 450 { 451 static if (is(typeof(__traits(getMember, This, m)) == SymbolCache)) 452 { 453 __traits(getMember, This, m) = new StringTable!(Symbol*)(); 454 __traits(getMember, This, m)._init(); 455 } 456 } 457 } 458 459 /// Resets the symbols. 460 void reset() 461 { 462 clearCache(); 463 resetSymbolCache(); 464 } 465 466 // Clears any caches. 467 private void clearCache() 468 { 469 alias This = typeof(this); 470 471 foreach (m ; __traits(allMembers, This)) 472 { 473 static if (is(typeof(__traits(getMember, This, m)) == Symbol*)) 474 __traits(getMember, This, m) = null; 475 } 476 } 477 478 // Resets the symbol caches. 479 private void resetSymbolCache() 480 { 481 alias This = typeof(this); 482 483 foreach (m ; __traits(allMembers, This)) 484 { 485 static if (is(typeof(__traits(getMember, This, m)) == SymbolCache)) 486 __traits(getMember, This, m).reset(); 487 } 488 } 489 490 bool hasSymbols() 491 { 492 if (hasSymbols_) 493 return true; 494 495 alias This = typeof(this); 496 497 foreach (m ; __traits(allMembers, This)) 498 { 499 static if (is(typeof(__traits(getMember, This, m)) == Symbol*)) 500 { 501 if (__traits(getMember, This, m) !is null) 502 return true; 503 } 504 } 505 506 return false; 507 } 508 509 /** 510 * Convenience wrapper around `dmd.backend.global.symbol_name`. 511 * 512 * Allows to pass the name of the symbol as a D string. 513 */ 514 Symbol* symbolName(const(char)[] name, int sclass, type* t) 515 { 516 return symbol_name(name.ptr, cast(uint) name.length, sclass, t); 517 } 518 519 /** 520 * Gets a global symbol. 521 * 522 * Params: 523 * name = the name of the symbol 524 * t = the type of the symbol 525 * 526 * Returns: the symbol 527 */ 528 Symbol* getGlobal(const(char)[] name, type* t = type_fake(TYnptr)) 529 { 530 return symbolName(name, SCglobal, t); 531 } 532 533 /** 534 * Gets a static symbol. 535 * 536 * Params: 537 * name = the name of the symbol 538 * t = the type of the symbol 539 * 540 * Returns: the symbol 541 */ 542 Symbol* getStatic(const(char)[] name, type* t = type_fake(TYnptr)) 543 { 544 return symbolName(name, SCstatic, t); 545 } 546 547 Symbol* getCString(const(char)[] str, const(char)[] symbolName, Segments.Id segment) 548 { 549 hasSymbols_ = true; 550 551 // create data 552 auto dtb = DtBuilder(0); 553 dtb.nbytes(cast(uint) (str.length + 1), str.toStringz()); 554 555 // find segment 556 auto seg = Segments[segment]; 557 558 // create symbol 559 auto s = getStatic(symbolName, type_allocn(TYarray, tstypes[TYchar])); 560 s.Sdt = dtb.finish(); 561 s.Sseg = seg; 562 return s; 563 } 564 565 Symbol* getMethVarName(const(char)[] name) 566 { 567 return cache(name, methVarNameTable, { 568 __gshared size_t classNameCount = 0; 569 char[42] buffer; 570 const symbolName = format(buffer, "L_OBJC_METH_VAR_NAME_%lu", classNameCount++); 571 572 return getCString(name, symbolName, Segments.Id.methname); 573 }); 574 } 575 576 Symbol* getMethVarName(Identifier ident) 577 { 578 return getMethVarName(ident.toString()); 579 } 580 581 Symbol* getMsgSend(Type returnType, bool hasHiddenArgument) 582 { 583 if (hasHiddenArgument) 584 return setMsgSendSymbol!("_objc_msgSend_stret")(TYhfunc); 585 // not sure if DMD can handle this 586 else if (returnType.ty == Tcomplex80) 587 return setMsgSendSymbol!("_objc_msgSend_fp2ret"); 588 else if (returnType.ty == Tfloat80) 589 return setMsgSendSymbol!("_objc_msgSend_fpret"); 590 else 591 return setMsgSendSymbol!("_objc_msgSend"); 592 593 assert(0); 594 } 595 596 Symbol* getMsgSendSuper(bool hasHiddenArgument) 597 { 598 if (hasHiddenArgument) 599 return setMsgSendSymbol!("_objc_msgSendSuper_stret")(TYhfunc); 600 else 601 return setMsgSendSymbol!("_objc_msgSendSuper")(TYnfunc); 602 } 603 604 Symbol* getImageInfo() 605 { 606 if (imageInfo) 607 return imageInfo; 608 609 auto dtb = DtBuilder(0); 610 dtb.dword(0); // version 611 dtb.dword(64); // flags 612 613 imageInfo = symbol_name("L_OBJC_IMAGE_INFO", SCstatic, type_allocn(TYarray, tstypes[TYchar])); 614 imageInfo.Sdt = dtb.finish(); 615 imageInfo.Sseg = Segments[Segments.Id.imageinfo]; 616 outdata(imageInfo); 617 618 return imageInfo; 619 } 620 621 Symbol* getModuleInfo(/*const*/ ref ClassDeclarations classes, 622 /*const*/ ref ClassDeclarations categories) 623 { 624 assert(!moduleInfo); // only allow once per object file 625 626 auto dtb = DtBuilder(0); 627 628 foreach (c; classes) 629 dtb.xoff(getClassName(c), 0); 630 631 foreach (c; categories) 632 dtb.xoff(getClassName(c), 0); 633 634 Symbol* symbol = symbol_name("L_OBJC_LABEL_CLASS_$", SCstatic, type_allocn(TYarray, tstypes[TYchar])); 635 symbol.Sdt = dtb.finish(); 636 symbol.Sseg = Segments[Segments.Id.classlist]; 637 outdata(symbol); 638 639 getImageInfo(); // make sure we also generate image info 640 641 return moduleInfo; 642 } 643 644 /** 645 * Returns: the `_OBJC_METACLASS_$_`/`_OBJC_CLASS_$_` symbol for the given 646 * class declaration. 647 */ 648 Symbol* getClassName(ObjcClassDeclaration objcClass) 649 { 650 hasSymbols_ = true; 651 652 const prefix = objcClass.isMeta ? "_OBJC_METACLASS_$_" : "_OBJC_CLASS_$_"; 653 auto name = prefix ~ objcClass.classDeclaration.objc.identifier.toString(); 654 655 return cache(name, classNameTable, () => getGlobal(name)); 656 } 657 658 /// ditto 659 Symbol* getClassName(ClassDeclaration classDeclaration, bool isMeta = false) 660 in 661 { 662 assert(classDeclaration !is null); 663 } 664 do 665 { 666 return getClassName(ObjcClassDeclaration(classDeclaration, isMeta)); 667 } 668 669 /* 670 * Returns: the `L_OBJC_CLASSLIST_REFERENCES_$_` symbol for the given class 671 * declaration. 672 */ 673 Symbol* getClassReference(ClassDeclaration classDeclaration) 674 { 675 hasSymbols_ = true; 676 677 auto name = classDeclaration.objc.identifier.toString(); 678 679 return cache(name, classReferenceTable, { 680 auto dtb = DtBuilder(0); 681 auto className = getClassName(classDeclaration); 682 dtb.xoff(className, 0, TYnptr); 683 684 auto segment = Segments[Segments.Id.classrefs]; 685 686 __gshared size_t classReferenceCount = 0; 687 688 char[42] nameString; 689 auto result = format(nameString, "L_OBJC_CLASSLIST_REFERENCES_$_%lu", classReferenceCount++); 690 auto symbol = getStatic(result); 691 symbol.Sdt = dtb.finish(); 692 symbol.Sseg = segment; 693 outdata(symbol); 694 695 return symbol; 696 }); 697 } 698 699 Symbol* getMethVarRef(const(char)[] name) 700 { 701 return cache(name, methVarRefTable, { 702 // create data 703 auto dtb = DtBuilder(0); 704 auto selector = getMethVarName(name); 705 dtb.xoff(selector, 0, TYnptr); 706 707 // find segment 708 auto seg = Segments[Segments.Id.selrefs]; 709 710 // create symbol 711 __gshared size_t selectorCount = 0; 712 char[42] nameString; 713 sprintf(nameString.ptr, "L_OBJC_SELECTOR_REFERENCES_%llu", cast(ulong) selectorCount); 714 auto symbol = symbol_name(nameString.ptr, SCstatic, type_fake(TYnptr)); 715 716 symbol.Sdt = dtb.finish(); 717 symbol.Sseg = seg; 718 outdata(symbol); 719 720 ++selectorCount; 721 722 return symbol; 723 }); 724 } 725 726 Symbol* getMethVarRef(const Identifier ident) 727 { 728 return getMethVarRef(ident.toString()); 729 } 730 731 /** 732 * Returns the Objective-C type encoding for the given type. 733 * 734 * The available type encodings are documented by Apple, available at 735 * $(LINK2 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100, Type Encoding). 736 * The type encodings can also be obtained by running an Objective-C 737 * compiler and using the `@encode()` compiler directive. 738 * 739 * Params: 740 * type = the type to return the type encoding for 741 * 742 * Returns: a string containing the type encoding 743 */ 744 string getTypeEncoding(Type type) 745 in 746 { 747 assert(type !is null); 748 } 749 do 750 { 751 enum assertMessage = "imaginary types are not supported by Objective-C"; 752 753 with (ENUMTY) switch (type.ty) 754 { 755 case Tvoid: return "v"; 756 case Tbool: return "B"; 757 case Tint8: return "c"; 758 case Tuns8: return "C"; 759 case Tchar: return "C"; 760 case Tint16: return "s"; 761 case Tuns16: return "S"; 762 case Twchar: return "S"; 763 case Tint32: return "i"; 764 case Tuns32: return "I"; 765 case Tdchar: return "I"; 766 case Tint64: return "q"; 767 case Tuns64: return "Q"; 768 case Tfloat32: return "f"; 769 case Tcomplex32: return "jf"; 770 case Tfloat64: return "d"; 771 case Tcomplex64: return "jd"; 772 case Tfloat80: return "D"; 773 case Tcomplex80: return "jD"; 774 case Timaginary32: assert(false, assertMessage); 775 case Timaginary64: assert(false, assertMessage); 776 case Timaginary80: assert(false, assertMessage); 777 default: return "?"; // unknown 778 // TODO: add "*" char*, "#" Class, "@" id, ":" SEL 779 // TODO: add "^"<type> indirection and "^^" double indirection 780 } 781 } 782 783 /** 784 * Returns: the `L_OBJC_METH_VAR_TYPE_` symbol containing the given 785 * type encoding. 786 */ 787 Symbol* getMethVarType(const(char)[] typeEncoding) 788 { 789 return cache(typeEncoding, methVarTypeTable, { 790 __gshared size_t count = 0; 791 char[42] nameString; 792 const symbolName = format(nameString, "L_OBJC_METH_VAR_TYPE_%lu", count++); 793 auto symbol = getCString(typeEncoding, symbolName, Segments.Id.methtype); 794 795 outdata(symbol); 796 797 return symbol; 798 }); 799 } 800 801 /// ditto 802 Symbol* getMethVarType(Type[] types ...) 803 { 804 string typeCode; 805 typeCode.reserve(types.length); 806 807 foreach (type; types) 808 typeCode ~= getTypeEncoding(type); 809 810 return getMethVarType(typeCode); 811 } 812 813 /// ditto 814 Symbol* getMethVarType(FuncDeclaration func) 815 { 816 Type[] types = [func.type.nextOf]; // return type first 817 818 if (func.parameters) 819 { 820 types.reserve(func.parameters.length); 821 822 foreach (e; *func.parameters) 823 types ~= e.type; 824 } 825 826 return getMethVarType(types); 827 } 828 829 /// Returns: the externally defined `__objc_empty_cache` symbol 830 Symbol* getEmptyCache() 831 { 832 return emptyCache = emptyCache ? emptyCache : getGlobal("__objc_empty_cache"); 833 } 834 835 /// Returns: the externally defined `__objc_empty_vtable` symbol 836 Symbol* getEmptyVTable() 837 { 838 return emptyVTable = emptyVTable ? emptyVTable : getGlobal("__objc_empty_vtable"); 839 } 840 841 /// Returns: the `L_OBJC_CLASS_NAME_` symbol for a class with the given name 842 Symbol* getClassNameRo(const(char)[] name) 843 { 844 return cache(name, classNameTable, { 845 __gshared size_t count = 0; 846 char[42] nameString; 847 const symbolName = format(nameString, "L_OBJC_CLASS_NAME_%lu", count++); 848 849 return getCString(name, symbolName, Segments.Id.classname); 850 }); 851 } 852 853 /// ditto 854 Symbol* getClassNameRo(const Identifier ident) 855 { 856 return getClassNameRo(ident.toString()); 857 } 858 859 Symbol* getIVarOffset(ClassDeclaration cd, VarDeclaration var, bool outputSymbol) 860 { 861 hasSymbols_ = true; 862 863 const className = cd.objc.identifier.toString; 864 const varName = var.ident.toString; 865 const name = "_OBJC_IVAR_$_" ~ className ~ '.' ~ varName; 866 867 auto stringValue = ivarOffsetTable.update(name); 868 auto symbol = stringValue.value; 869 870 if (!symbol) 871 { 872 symbol = getGlobal(name); 873 symbol.Sfl |= FLextern; 874 stringValue.value = symbol; 875 } 876 877 if (!outputSymbol) 878 return symbol; 879 880 auto dtb = DtBuilder(0); 881 dtb.size(var.offset); 882 883 symbol.Sdt = dtb.finish(); 884 symbol.Sseg = Segments[Segments.Id.ivar]; 885 symbol.Sfl &= ~FLextern; 886 887 outdata(symbol); 888 889 return symbol; 890 } 891 892 private Symbol* setMsgSendSymbol(string name)(tym_t ty = TYnfunc) 893 { 894 alias This = typeof(this); 895 enum fieldName = name[1 .. $]; 896 897 if (!__traits(getMember, This, fieldName)) 898 __traits(getMember, This, fieldName) = getGlobal(name, type_fake(ty)); 899 900 return __traits(getMember, This, fieldName); 901 } 902 903 /** 904 * Caches the symbol returned by `block` using the given name. 905 * 906 * If the symbol is already in the cache, the symbol will be returned 907 * immediately and `block` will not be called. 908 * 909 * Params: 910 * name = the name to cache the symbol under 911 * symbolCache = the cache storage to use for this symbol 912 * block = invoked when the symbol is not in the cache. The return value 913 * will be put into the cache 914 * 915 * Returns: the cached symbol 916 */ 917 private Symbol* cache(const(char)[] name, SymbolCache symbolCache, 918 scope Symbol* delegate() block) 919 { 920 hasSymbols_ = true; 921 922 auto stringValue = symbolCache.update(name); 923 924 if (stringValue.value) 925 return stringValue.value; 926 927 return stringValue.value = block(); 928 } 929 } 930 931 private: 932 933 /** 934 * Functionality for outputting symbols for a specific Objective-C class 935 * declaration. 936 */ 937 struct ObjcClassDeclaration 938 { 939 /// Indicates what kind of class this is. 940 private enum Flags 941 { 942 /// Regular class. 943 regular = 0x00000, 944 945 /// Meta class. 946 meta = 0x00001, 947 948 /// Root class. A class without any base class. 949 root = 0x00002 950 } 951 952 /// The class declaration 953 ClassDeclaration classDeclaration; 954 955 /// `true` if this class is a metaclass. 956 bool isMeta; 957 958 this(ClassDeclaration classDeclaration, bool isMeta) 959 in 960 { 961 assert(classDeclaration !is null); 962 } 963 do 964 { 965 this.classDeclaration = classDeclaration; 966 this.isMeta = isMeta; 967 } 968 969 /** 970 * Outputs the class declaration to the object file. 971 * 972 * Returns: the exported symbol, that is, `_OBJC_METACLASS_$_` or 973 * `_OBJC_CLASS_$_` 974 */ 975 Symbol* toObjFile() 976 { 977 if (classDeclaration.objc.isExtern) 978 return null; // only a declaration for an externally-defined class 979 980 auto dtb = DtBuilder(0); 981 toDt(dtb); 982 983 auto symbol = Symbols.getClassName(this); 984 symbol.Sdt = dtb.finish(); 985 symbol.Sseg = Segments[Segments.Id.data]; 986 outdata(symbol); 987 988 return symbol; 989 } 990 991 private: 992 993 /** 994 * Outputs the class declaration to the object file. 995 * 996 * Params: 997 * dtb = the `DtBuilder` to output the class declaration to 998 */ 999 void toDt(ref DtBuilder dtb) 1000 { 1001 auto baseClassSymbol = classDeclaration.baseClass ? 1002 Symbols.getClassName(classDeclaration.baseClass, isMeta) : null; 1003 1004 dtb.xoff(getMetaclass(), 0); // pointer to metaclass 1005 dtb.xoffOrNull(baseClassSymbol); // pointer to base class 1006 dtb.xoff(Symbols.getEmptyCache(), 0); 1007 dtb.xoff(Symbols.getEmptyVTable(), 0); 1008 dtb.xoff(getClassRo(), 0); 1009 } 1010 1011 /// Returns: the name of the metaclass of this class declaration 1012 Symbol* getMetaclass() 1013 { 1014 if (isMeta) 1015 { 1016 // metaclass: return root class's name 1017 // (will be replaced with metaclass reference at load) 1018 1019 auto metaclassDeclaration = classDeclaration; 1020 1021 while (metaclassDeclaration.baseClass) 1022 metaclassDeclaration = metaclassDeclaration.baseClass; 1023 1024 return Symbols.getClassName(metaclassDeclaration, true); 1025 } 1026 1027 else 1028 { 1029 // regular class: return metaclass with the same name 1030 return ObjcClassDeclaration(classDeclaration, true).toObjFile(); 1031 } 1032 } 1033 1034 /** 1035 * Returns: the `l_OBJC_CLASS_RO_$_`/`l_OBJC_METACLASS_RO_$_` symbol for 1036 * this class declaration 1037 */ 1038 Symbol* getClassRo() 1039 { 1040 auto dtb = DtBuilder(0); 1041 1042 dtb.dword(flags); 1043 dtb.dword(instanceStart); 1044 dtb.dword(instanceSize); 1045 dtb.dword(0); // reserved 1046 1047 dtb.size(0); // ivar layout 1048 dtb.xoff(Symbols.getClassNameRo(classDeclaration.ident), 0); // name of the class 1049 1050 dtb.xoffOrNull(getMethodList()); // instance method list 1051 dtb.xoffOrNull(getProtocolList()); // protocol list 1052 1053 if (isMeta) 1054 { 1055 dtb.size(0); // instance variable list 1056 dtb.size(0); // weak ivar layout 1057 dtb.size(0); // properties 1058 } 1059 1060 else 1061 { 1062 dtb.xoffOrNull(getIVarList()); // instance variable list 1063 dtb.size(0); // weak ivar layout 1064 dtb.xoffOrNull(getPropertyList()); // properties 1065 } 1066 1067 const prefix = isMeta ? "l_OBJC_METACLASS_RO_$_" : "l_OBJC_CLASS_RO_$_"; 1068 const symbolName = prefix ~ classDeclaration.objc.identifier.toString(); 1069 auto symbol = Symbols.getStatic(symbolName); 1070 1071 symbol.Sdt = dtb.finish(); 1072 symbol.Sseg = Segments[Segments.Id.const_]; 1073 outdata(symbol); 1074 1075 return symbol; 1076 } 1077 1078 /** 1079 * Returns method list for this class declaration. 1080 * 1081 * This is a list of all methods defined in this class declaration, i.e. 1082 * methods with a body. 1083 * 1084 * Returns: the symbol for the method list, `l_OBJC_$_CLASS_METHODS_` or 1085 * `l_OBJC_$_INSTANCE_METHODS_` 1086 */ 1087 Symbol* getMethodList() 1088 { 1089 /** 1090 * Returns the number of methods that should be added to the binary. 1091 * 1092 * Only methods with a body should be added. 1093 * 1094 * Params: 1095 * members = the members of the class declaration 1096 */ 1097 static int methodCount(FuncDeclarations* methods) 1098 { 1099 int count; 1100 1101 methods.each!((method) { 1102 if (method.fbody) 1103 count++; 1104 }); 1105 1106 return count; 1107 } 1108 1109 auto methods = isMeta ? classDeclaration.objc.metaclass.objc.methodList : 1110 classDeclaration.objc.methodList; 1111 1112 const count = methodCount(methods); 1113 1114 if (count == 0) 1115 return null; 1116 1117 auto dtb = DtBuilder(0); 1118 1119 dtb.dword(24); // _objc_method.sizeof 1120 dtb.dword(count); // method count 1121 1122 methods.each!((func) { 1123 if (func.fbody) 1124 { 1125 assert(func.objc.selector); 1126 dtb.xoff(func.objc.selector.toNameSymbol(), 0); // method name 1127 dtb.xoff(Symbols.getMethVarType(func), 0); // method type string 1128 dtb.xoff(func.toSymbol(), 0); // function implementation 1129 } 1130 }); 1131 1132 const prefix = isMeta ? "l_OBJC_$_CLASS_METHODS_" : "l_OBJC_$_INSTANCE_METHODS_"; 1133 const symbolName = prefix ~ classDeclaration.objc.identifier.toString(); 1134 auto symbol = Symbols.getStatic(symbolName); 1135 1136 symbol.Sdt = dtb.finish(); 1137 symbol.Sseg = Segments[Segments.Id.const_]; 1138 1139 return symbol; 1140 } 1141 1142 Symbol* getProtocolList() 1143 { 1144 // protocols are not supported yet 1145 return null; 1146 } 1147 1148 Symbol* getIVarList() 1149 { 1150 if (isMeta || classDeclaration.fields.length == 0) 1151 return null; 1152 1153 auto dtb = DtBuilder(0); 1154 1155 dtb.dword(32); // entsize, _ivar_t.sizeof 1156 dtb.dword(cast(int) classDeclaration.fields.length); // ivar count 1157 1158 foreach (field; classDeclaration.fields) 1159 { 1160 auto var = field.isVarDeclaration; 1161 assert(var); 1162 assert((var.storage_class & STC.static_) == 0); 1163 1164 dtb.xoff(Symbols.getIVarOffset(classDeclaration, var, true), 0); // pointer to ivar offset 1165 dtb.xoff(Symbols.getMethVarName(var.ident), 0); // name 1166 dtb.xoff(Symbols.getMethVarType(var.type), 0); // type string 1167 dtb.dword(var.alignment); 1168 dtb.dword(cast(int) var.size(var.loc)); 1169 } 1170 1171 enum prefix = "l_OBJC_$_INSTANCE_VARIABLES_"; 1172 const symbolName = prefix ~ classDeclaration.objc.identifier.toString(); 1173 auto symbol = Symbols.getStatic(symbolName); 1174 1175 symbol.Sdt = dtb.finish(); 1176 symbol.Sseg = Segments[Segments.Id.const_]; 1177 1178 return symbol; 1179 } 1180 1181 Symbol* getPropertyList() 1182 { 1183 // properties are not supported yet 1184 return null; 1185 } 1186 1187 Symbol* getIVarOffset(VarDeclaration var) 1188 { 1189 if (var.toParent() is classDeclaration) 1190 return Symbols.getIVarOffset(classDeclaration, var, false); 1191 1192 else if (classDeclaration.baseClass) 1193 return ObjcClassDeclaration(classDeclaration.baseClass, false) 1194 .getIVarOffset(var); 1195 1196 else 1197 assert(false, "Trying to get the base class of a root class"); 1198 } 1199 1200 /** 1201 * Returns the flags for this class declaration. 1202 * 1203 * That is, if this is a regular class, a metaclass and/or a root class. 1204 * 1205 * Returns: the flags 1206 */ 1207 uint flags() const 1208 { 1209 uint flags = isMeta ? Flags.meta : Flags.regular; 1210 1211 if (classDeclaration.objc.isRootClass) 1212 flags |= Flags.root; 1213 1214 return flags; 1215 } 1216 1217 /** 1218 * Returns the offset of where an instance of this class starts. 1219 * 1220 * For a metaclass this is always `40`. For a class with no instance 1221 * variables this is the size of the class declaration. For a class with 1222 * instance variables it's the offset of the first instance variable. 1223 * 1224 * Returns: the instance start 1225 */ 1226 int instanceStart() 1227 { 1228 if (isMeta) 1229 return 40; 1230 1231 const start = cast(uint) classDeclaration.size(classDeclaration.loc); 1232 1233 if (!classDeclaration.members || classDeclaration.members.length == 0) 1234 return start; 1235 1236 foreach (member; *classDeclaration.members) 1237 { 1238 auto var = member.isVarDeclaration; 1239 1240 if (var && var.isField) 1241 return var.offset; 1242 } 1243 1244 return start; 1245 } 1246 1247 /// Returns: the size of an instance of this class 1248 int instanceSize() 1249 { 1250 return isMeta ? 40 : cast(int) classDeclaration.size(classDeclaration.loc); 1251 } 1252 } 1253 1254 /* 1255 * Formats the given arguments into the given buffer. 1256 * 1257 * Convenience wrapper around `snprintf`. 1258 * 1259 * Params: 1260 * bufLength = length of the buffer 1261 * buffer = the buffer where to store the result 1262 * format = the format string 1263 * args = the arguments to format 1264 * 1265 * Returns: the formatted result, a slice of the given buffer 1266 */ 1267 char[] format(size_t bufLength, Args...)(return ref char[bufLength] buffer, 1268 const(char)* format, const Args args) 1269 { 1270 auto length = snprintf(buffer.ptr, buffer.length, format, args); 1271 1272 assert(length >= 0, "An output error occurred"); 1273 assert(length < buffer.length, "Output was truncated"); 1274 1275 return buffer[0 .. length]; 1276 } 1277 1278 /// Returns: the symbol of the given selector 1279 Symbol* toNameSymbol(const ObjcSelector* selector) 1280 { 1281 return Symbols.getMethVarName(selector.toString()); 1282 } 1283 1284 /** 1285 * Adds a reference to the given `symbol` or null if the symbol is null. 1286 * 1287 * Params: 1288 * dtb = the dt builder to add the symbol to 1289 * symbol = the symbol to add 1290 */ 1291 void xoffOrNull(ref DtBuilder dtb, Symbol* symbol) 1292 { 1293 if (symbol) 1294 dtb.xoff(symbol, 0); 1295 else 1296 dtb.size(0); 1297 } 1298 1299 /** 1300 * Converts the given D string to a null terminated C string. 1301 * 1302 * Asserts if `str` is longer than `maxLength`, with assertions enabled. With 1303 * assertions disabled it will truncate the result to `maxLength`. 1304 * 1305 * Params: 1306 * maxLength = the max length of `str` 1307 * str = the string to convert 1308 * buf = the buffer where to allocate the result. By default this will be 1309 * allocated in the caller scope using `alloca`. If the buffer is created 1310 * by the callee it needs to be able to fit at least `str.length + 1` bytes 1311 * 1312 * Returns: the given string converted to a C string, a slice of `str` or the 1313 * given buffer `buffer` 1314 */ 1315 const(char)* toStringz(size_t maxLength = 4095)(in const(char)[] str, 1316 scope return void[] buffer = alloca(maxLength + 1)[0 .. maxLength + 1]) pure 1317 in 1318 { 1319 assert(maxLength >= str.length); 1320 } 1321 out(result) 1322 { 1323 assert(str.length == result.strlen); 1324 } 1325 do 1326 { 1327 if (str.length == 0) 1328 return "".ptr; 1329 1330 const maxLength = buffer.length - 1; 1331 const len = str.length > maxLength ? maxLength : str.length; 1332 auto buf = cast(char[]) buffer[0 .. len + 1]; 1333 buf[0 .. len] = str[0 .. len]; 1334 buf[len] = '\0'; 1335 1336 return cast(const(char)*) buf.ptr; 1337 }