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 34 import dmd.backend.dt; 35 import dmd.backend.cc; 36 import dmd.backend.cdef; 37 import dmd.backend.el; 38 import dmd.backend.global; 39 import dmd.backend.oper; 40 import dmd.backend.outbuf; 41 import dmd.backend.ty; 42 import dmd.backend.type; 43 import dmd.backend.mach; 44 import dmd.backend.obj; 45 46 private __gshared ObjcGlue _objc; 47 48 ObjcGlue objc() 49 { 50 return _objc; 51 } 52 53 // Should be an interface 54 extern(C++) abstract class ObjcGlue 55 { 56 static struct ElemResult 57 { 58 elem* ec; 59 elem* ethis; 60 } 61 62 static void initialize() 63 { 64 if (target.objc.supported) 65 _objc = new Supported; 66 else 67 _objc = new Unsupported; 68 } 69 70 abstract void setupMethodSelector(FuncDeclaration fd, elem** esel); 71 72 abstract ElemResult setupMethodCall(FuncDeclaration fd, TypeFunction tf, 73 bool directcall, elem* ec, elem* ehidden, elem* ethis); 74 75 abstract void setupEp(elem* esel, elem** ep, int leftToRight); 76 abstract void generateModuleInfo(Module module_); 77 78 /// Returns: the given expression converted to an `elem` structure 79 abstract elem* toElem(ObjcClassReferenceExp e) const; 80 81 /// Outputs the given Objective-C class to the object file. 82 abstract void toObjFile(ClassDeclaration classDeclaration) const; 83 84 /** 85 * Adds the selector parameter to the given list of parameters. 86 * 87 * For Objective-C methods the selector parameter is added. For 88 * non-Objective-C methods `parameters` is unchanged. 89 * 90 * Params: 91 * functionDeclaration = the function declaration to add the selector 92 * parameter from 93 * parameters = the list of parameters to add the selector parameter to 94 * parameterCount = the number of parameters 95 * 96 * Returns: the new number of parameters 97 */ 98 abstract size_t addSelectorParameterSymbol( 99 FuncDeclaration functionDeclaration, 100 Symbol** parameters, size_t parameterCount) const; 101 102 /** 103 * Returns the offset of the given variable declaration `var`. 104 * 105 * This is used in a `DotVarExp` to get the offset of the variable the 106 * expression is accessing. 107 * 108 * Instance variables in Objective-C are non-fragile. That means that the 109 * base class can change (add or remove instance variables) without the 110 * subclasses needing to recompile or relink. This is implemented instance 111 * variables having a dynamic offset. This is achieved by going through an 112 * indirection in the form of a symbol generated in the binary. The compiler 113 * outputs the static offset in the generated symbol. Then, at load time, 114 * the symbol is updated with the correct offset, if necessary. 115 * 116 * Params: 117 * var = the variable declaration to return the offset of 118 * type = the type of the `DotVarExp` 119 * offset = the existing offset 120 * 121 * Returns: a symbol containing the offset of the variable declaration 122 */ 123 abstract elem* getOffset(VarDeclaration var, Type type, elem* offset) const; 124 } 125 126 private: 127 128 extern(C++) final class Unsupported : ObjcGlue 129 { 130 override void setupMethodSelector(FuncDeclaration fd, elem** esel) 131 { 132 // noop 133 } 134 135 override ElemResult setupMethodCall(FuncDeclaration, TypeFunction, bool, 136 elem*, elem*, elem*) 137 { 138 assert(0, "Should never be called when Objective-C is not supported"); 139 } 140 141 override void setupEp(elem* esel, elem** ep, int reverse) 142 { 143 // noop 144 } 145 146 override void generateModuleInfo(Module) 147 { 148 // noop 149 } 150 151 override elem* toElem(ObjcClassReferenceExp e) const 152 { 153 assert(0, "Should never be called when Objective-C is not supported"); 154 } 155 156 override void toObjFile(ClassDeclaration classDeclaration) const 157 { 158 assert(0, "Should never be called when Objective-C is not supported"); 159 } 160 161 override size_t addSelectorParameterSymbol(FuncDeclaration, Symbol**, 162 size_t count) const 163 { 164 return count; 165 } 166 167 override elem* getOffset(VarDeclaration var, Type type, elem* offset) const 168 { 169 return offset; 170 } 171 } 172 173 extern(C++) final class Supported : ObjcGlue 174 { 175 extern (D) this() 176 { 177 Segments.initialize(); 178 Symbols.initialize(); 179 } 180 181 override void setupMethodSelector(FuncDeclaration fd, elem** esel) 182 { 183 if (fd && fd.selector && !*esel) 184 { 185 *esel = el_var(Symbols.getMethVarRef(fd.selector.toString())); 186 } 187 } 188 189 override ElemResult setupMethodCall(FuncDeclaration fd, TypeFunction tf, 190 bool directcall, elem* ec, elem* ehidden, elem* ethis) 191 { 192 import dmd.e2ir : addressElem; 193 194 if (directcall) // super call 195 { 196 ElemResult result; 197 // call through Objective-C runtime dispatch 198 result.ec = el_var(Symbols.getMsgSendSuper(ehidden !is null)); 199 200 // need to change this pointer to a pointer to an two-word 201 // objc_super struct of the form { this ptr, class ptr }. 202 auto cd = fd.isThis.isClassDeclaration; 203 assert(cd, "call to objc_msgSendSuper with no class declaration"); 204 205 // faking objc_super type as delegate 206 auto classRef = el_var(Symbols.getClassReference(cd)); 207 auto super_ = el_pair(TYdelegate, ethis, classRef); 208 209 result.ethis = addressElem(super_, tf); 210 211 return result; 212 } 213 214 else 215 { 216 // make objc-style "virtual" call using dispatch function 217 assert(ethis); 218 Type tret = tf.next; 219 220 ElemResult result = { 221 ec: el_var(Symbols.getMsgSend(tret, ehidden !is null)), 222 ethis: ethis 223 }; 224 225 return result; 226 } 227 } 228 229 override void setupEp(elem* esel, elem** ep, int leftToRight) 230 { 231 if (esel) 232 { 233 // using objc-style "virtual" call 234 // add hidden argument (second to 'this') for selector used by dispatch function 235 if (leftToRight) 236 *ep = el_param(esel, *ep); 237 else 238 *ep = el_param(*ep, esel); 239 } 240 } 241 242 override void generateModuleInfo(Module module_) 243 { 244 ClassDeclarations classes; 245 ClassDeclarations categories; 246 247 module_.members.foreachDsymbol(m => m.addObjcSymbols(&classes, &categories)); 248 249 if (classes.length || categories.length || Symbols.hasSymbols) 250 Symbols.getModuleInfo(classes, categories); 251 } 252 253 override elem* toElem(ObjcClassReferenceExp e) const 254 { 255 return el_var(Symbols.getClassReference(e.classDeclaration)); 256 } 257 258 override void toObjFile(ClassDeclaration classDeclaration) const 259 in 260 { 261 assert(classDeclaration !is null); 262 assert(classDeclaration.classKind == ClassKind.objc); 263 } 264 do 265 { 266 if (!classDeclaration.objc.isMeta) 267 ObjcClassDeclaration(classDeclaration, false).toObjFile(); 268 } 269 270 override size_t addSelectorParameterSymbol(FuncDeclaration fd, 271 Symbol** params, size_t count) const 272 in 273 { 274 assert(fd); 275 } 276 do 277 { 278 if (!fd.selector) 279 return count; 280 281 assert(fd.selectorParameter); 282 auto selectorSymbol = fd.selectorParameter.toSymbol(); 283 memmove(params + 1, params, count * params[0].sizeof); 284 params[0] = selectorSymbol; 285 286 return count + 1; 287 } 288 289 override elem* getOffset(VarDeclaration var, Type type, elem* offset) const 290 { 291 auto typeClass = type.isTypeClass; 292 293 if (!typeClass || typeClass.sym.classKind != ClassKind.objc) 294 return offset; 295 296 return el_var(ObjcClassDeclaration(typeClass.sym, false).getIVarOffset(var)); 297 } 298 } 299 300 struct Segments 301 { 302 enum Id 303 { 304 classlist, 305 classname, 306 classrefs, 307 const_, 308 data, 309 imageinfo, 310 ivar, 311 methname, 312 methtype, 313 selrefs 314 } 315 316 private 317 { 318 __gshared int[Id] segments; 319 __gshared Segments[Id] segmentData; 320 321 immutable(char*) sectionName; 322 immutable(char*) segmentName; 323 immutable int flags; 324 immutable int alignment; 325 326 this(typeof(this.tupleof) tuple) 327 { 328 this.tupleof = tuple; 329 } 330 331 static void initialize() 332 { 333 segmentData = [ 334 Id.classlist: Segments("__objc_classlist", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 3), 335 Id.classname: Segments("__objc_classname", "__TEXT", S_CSTRING_LITERALS, 0), 336 Id.classrefs: Segments("__objc_classrefs", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 3), 337 Id.const_: Segments("__objc_const", "__DATA", S_REGULAR, 3), 338 Id.data: Segments("__objc_data", "__DATA", S_REGULAR, 3), 339 Id.imageinfo: Segments("__objc_imageinfo", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 0), 340 Id.ivar: Segments("__objc_ivar", "__DATA", S_REGULAR, 3), 341 Id.methname: Segments("__objc_methname", "__TEXT", S_CSTRING_LITERALS, 0), 342 Id.methtype: Segments("__objc_methtype", "__TEXT", S_CSTRING_LITERALS, 0), 343 Id.selrefs: Segments("__objc_selrefs", "__DATA", S_LITERAL_POINTERS | S_ATTR_NO_DEAD_STRIP, 3), 344 ]; 345 } 346 } 347 348 static int opIndex(Id id) 349 { 350 if (auto segment = id in segments) 351 return *segment; 352 353 const seg = segmentData[id]; 354 355 version (OSX) 356 { 357 return segments[id] = Obj.getsegment( 358 seg.sectionName, 359 seg.segmentName, 360 seg.alignment, 361 seg.flags 362 ); 363 } 364 365 else 366 { 367 // This should never happen. If the platform is not OSX an error 368 // should have occurred sooner which should have prevented the 369 // code from getting here. 370 assert(0); 371 } 372 } 373 } 374 375 struct Symbols 376 { 377 static: 378 379 private __gshared 380 { 381 bool hasSymbols_ = false; 382 383 Symbol* objc_msgSend = null; 384 Symbol* objc_msgSend_stret = null; 385 Symbol* objc_msgSend_fpret = null; 386 Symbol* objc_msgSend_fp2ret = null; 387 388 Symbol* objc_msgSendSuper = null; 389 Symbol* objc_msgSendSuper_stret = null; 390 391 Symbol* imageInfo = null; 392 Symbol* moduleInfo = null; 393 394 Symbol* emptyCache = null; 395 Symbol* emptyVTable = null; 396 397 // Cache for `_OBJC_METACLASS_$_`/`_OBJC_CLASS_$_` symbols. 398 StringTable!(Symbol*)* classNameTable = null; 399 400 // Cache for `L_OBJC_CLASSLIST_REFERENCES_` symbols. 401 StringTable!(Symbol*)* classReferenceTable = null; 402 403 StringTable!(Symbol*)* methVarNameTable = null; 404 StringTable!(Symbol*)* methVarRefTable = null; 405 StringTable!(Symbol*)* methVarTypeTable = null; 406 407 // Cache for instance variable offsets 408 StringTable!(Symbol*)* ivarOffsetTable = null; 409 } 410 411 void initialize() 412 { 413 initializeStringTables(); 414 } 415 416 private void initializeStringTables() 417 { 418 alias This = typeof(this); 419 420 foreach (m ; __traits(allMembers, This)) 421 { 422 static if (is(typeof(__traits(getMember, This, m)) == StringTable!(Symbol*)*)) 423 { 424 __traits(getMember, This, m) = new StringTable!(Symbol*)(); 425 __traits(getMember, This, m)._init(); 426 } 427 } 428 } 429 430 bool hasSymbols() 431 { 432 if (hasSymbols_) 433 return true; 434 435 alias This = typeof(this); 436 437 foreach (m ; __traits(allMembers, This)) 438 { 439 static if (is(typeof(__traits(getMember, This, m)) == Symbol*)) 440 { 441 if (__traits(getMember, This, m) !is null) 442 return true; 443 } 444 } 445 446 return false; 447 } 448 449 /** 450 * Convenience wrapper around `dmd.backend.global.symbol_name`. 451 * 452 * Allows to pass the name of the symbol as a D string. 453 */ 454 Symbol* symbolName(const(char)[] name, int sclass, type* t) 455 { 456 return symbol_name(name.ptr, cast(uint) name.length, sclass, t); 457 } 458 459 /** 460 * Gets a global symbol. 461 * 462 * Params: 463 * name = the name of the symbol 464 * t = the type of the symbol 465 * 466 * Returns: the symbol 467 */ 468 Symbol* getGlobal(const(char)[] name, type* t = type_fake(TYnptr)) 469 { 470 return symbolName(name, SCglobal, t); 471 } 472 473 /** 474 * Gets a static symbol. 475 * 476 * Params: 477 * name = the name of the symbol 478 * t = the type of the symbol 479 * 480 * Returns: the symbol 481 */ 482 Symbol* getStatic(const(char)[] name, type* t = type_fake(TYnptr)) 483 { 484 return symbolName(name, SCstatic, t); 485 } 486 487 Symbol* getCString(const(char)[] str, const(char)[] symbolName, Segments.Id segment) 488 { 489 hasSymbols_ = true; 490 491 // create data 492 auto dtb = DtBuilder(0); 493 dtb.nbytes(cast(uint) (str.length + 1), str.toStringz()); 494 495 // find segment 496 auto seg = Segments[segment]; 497 498 // create symbol 499 auto s = getStatic(symbolName, type_allocn(TYarray, tstypes[TYchar])); 500 s.Sdt = dtb.finish(); 501 s.Sseg = seg; 502 return s; 503 } 504 505 Symbol* getMethVarName(const(char)[] name) 506 { 507 hasSymbols_ = true; 508 509 auto stringValue = methVarNameTable.update(name); 510 auto symbol = stringValue.value; 511 512 if (!symbol) 513 { 514 __gshared size_t classNameCount = 0; 515 char[42] buffer; 516 const symbolName = format(buffer, "L_OBJC_METH_VAR_NAME_%lu", classNameCount++); 517 symbol = getCString(name, symbolName, Segments.Id.methname); 518 stringValue.value = symbol; 519 } 520 521 return symbol; 522 } 523 524 Symbol* getMethVarName(Identifier ident) 525 { 526 return getMethVarName(ident.toString()); 527 } 528 529 Symbol* getMsgSend(Type returnType, bool hasHiddenArgument) 530 { 531 if (hasHiddenArgument) 532 return setMsgSendSymbol!("_objc_msgSend_stret")(TYhfunc); 533 // not sure if DMD can handle this 534 else if (returnType.ty == Tcomplex80) 535 return setMsgSendSymbol!("_objc_msgSend_fp2ret"); 536 else if (returnType.ty == Tfloat80) 537 return setMsgSendSymbol!("_objc_msgSend_fpret"); 538 else 539 return setMsgSendSymbol!("_objc_msgSend"); 540 541 assert(0); 542 } 543 544 Symbol* getMsgSendSuper(bool hasHiddenArgument) 545 { 546 if (hasHiddenArgument) 547 return setMsgSendSymbol!("_objc_msgSendSuper_stret")(TYhfunc); 548 else 549 return setMsgSendSymbol!("_objc_msgSendSuper")(TYnfunc); 550 } 551 552 Symbol* getImageInfo() 553 { 554 if (imageInfo) 555 return imageInfo; 556 557 auto dtb = DtBuilder(0); 558 dtb.dword(0); // version 559 dtb.dword(64); // flags 560 561 imageInfo = symbol_name("L_OBJC_IMAGE_INFO", SCstatic, type_allocn(TYarray, tstypes[TYchar])); 562 imageInfo.Sdt = dtb.finish(); 563 imageInfo.Sseg = Segments[Segments.Id.imageinfo]; 564 outdata(imageInfo); 565 566 return imageInfo; 567 } 568 569 Symbol* getModuleInfo(/*const*/ ref ClassDeclarations classes, 570 /*const*/ ref ClassDeclarations categories) 571 { 572 assert(!moduleInfo); // only allow once per object file 573 574 auto dtb = DtBuilder(0); 575 576 foreach (c; classes) 577 dtb.xoff(getClassName(c), 0); 578 579 foreach (c; categories) 580 dtb.xoff(getClassName(c), 0); 581 582 Symbol* symbol = symbol_name("L_OBJC_LABEL_CLASS_$", SCstatic, type_allocn(TYarray, tstypes[TYchar])); 583 symbol.Sdt = dtb.finish(); 584 symbol.Sseg = Segments[Segments.Id.classlist]; 585 outdata(symbol); 586 587 getImageInfo(); // make sure we also generate image info 588 589 return moduleInfo; 590 } 591 592 /** 593 * Returns: the `_OBJC_METACLASS_$_`/`_OBJC_CLASS_$_` symbol for the given 594 * class declaration. 595 */ 596 Symbol* getClassName(ObjcClassDeclaration objcClass) 597 { 598 hasSymbols_ = true; 599 600 const prefix = objcClass.isMeta ? "_OBJC_METACLASS_$_" : "_OBJC_CLASS_$_"; 601 auto name = prefix ~ objcClass.classDeclaration.objc.identifier.toString(); 602 603 auto stringValue = classNameTable.update(name); 604 auto symbol = stringValue.value; 605 606 if (symbol) 607 return symbol; 608 609 symbol = getGlobal(name); 610 stringValue.value = symbol; 611 612 return symbol; 613 } 614 615 /// ditto 616 Symbol* getClassName(ClassDeclaration classDeclaration, bool isMeta = false) 617 in 618 { 619 assert(classDeclaration !is null); 620 } 621 do 622 { 623 return getClassName(ObjcClassDeclaration(classDeclaration, isMeta)); 624 } 625 626 /* 627 * Returns: the `L_OBJC_CLASSLIST_REFERENCES_$_` symbol for the given class 628 * declaration. 629 */ 630 Symbol* getClassReference(ClassDeclaration classDeclaration) 631 { 632 hasSymbols_ = true; 633 634 auto name = classDeclaration.objc.identifier.toString(); 635 636 auto stringValue = classReferenceTable.update(name); 637 auto symbol = stringValue.value; 638 639 if (symbol) 640 return symbol; 641 642 auto dtb = DtBuilder(0); 643 auto className = getClassName(classDeclaration); 644 dtb.xoff(className, 0, TYnptr); 645 646 auto segment = Segments[Segments.Id.classrefs]; 647 648 __gshared size_t classReferenceCount = 0; 649 650 char[42] nameString; 651 auto result = format(nameString, "L_OBJC_CLASSLIST_REFERENCES_$_%lu", classReferenceCount++); 652 symbol = getStatic(result); 653 symbol.Sdt = dtb.finish(); 654 symbol.Sseg = segment; 655 outdata(symbol); 656 657 stringValue.value = symbol; 658 659 return symbol; 660 } 661 662 Symbol* getMethVarRef(const(char)[] name) 663 { 664 hasSymbols_ = true; 665 666 auto stringValue = methVarRefTable.update(name); 667 auto refSymbol = stringValue.value; 668 if (refSymbol is null) 669 { 670 // create data 671 auto dtb = DtBuilder(0); 672 auto selector = getMethVarName(name); 673 dtb.xoff(selector, 0, TYnptr); 674 675 // find segment 676 auto seg = Segments[Segments.Id.selrefs]; 677 678 // create symbol 679 __gshared size_t selectorCount = 0; 680 char[42] nameString; 681 sprintf(nameString.ptr, "L_OBJC_SELECTOR_REFERENCES_%lu", selectorCount); 682 refSymbol = symbol_name(nameString.ptr, SCstatic, type_fake(TYnptr)); 683 684 refSymbol.Sdt = dtb.finish(); 685 refSymbol.Sseg = seg; 686 outdata(refSymbol); 687 stringValue.value = refSymbol; 688 689 ++selectorCount; 690 } 691 return refSymbol; 692 } 693 694 Symbol* getMethVarRef(const Identifier ident) 695 { 696 return getMethVarRef(ident.toString()); 697 } 698 699 /** 700 * Returns the Objective-C type encoding for the given type. 701 * 702 * The available type encodings are documented by Apple, available at 703 * $(LINK2 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100, Type Encoding). 704 * The type encodings can also be obtained by running an Objective-C 705 * compiler and using the `@encode()` compiler directive. 706 * 707 * Params: 708 * type = the type to return the type encoding for 709 * 710 * Returns: a string containing the type encoding 711 */ 712 string getTypeEncoding(Type type) 713 in 714 { 715 assert(type !is null); 716 } 717 do 718 { 719 enum assertMessage = "imaginary types are not supported by Objective-C"; 720 721 with (ENUMTY) switch (type.ty) 722 { 723 case Tvoid: return "v"; 724 case Tbool: return "B"; 725 case Tint8: return "c"; 726 case Tuns8: return "C"; 727 case Tchar: return "C"; 728 case Tint16: return "s"; 729 case Tuns16: return "S"; 730 case Twchar: return "S"; 731 case Tint32: return "i"; 732 case Tuns32: return "I"; 733 case Tdchar: return "I"; 734 case Tint64: return "q"; 735 case Tuns64: return "Q"; 736 case Tfloat32: return "f"; 737 case Tcomplex32: return "jf"; 738 case Tfloat64: return "d"; 739 case Tcomplex64: return "jd"; 740 case Tfloat80: return "D"; 741 case Tcomplex80: return "jD"; 742 case Timaginary32: assert(false, assertMessage); 743 case Timaginary64: assert(false, assertMessage); 744 case Timaginary80: assert(false, assertMessage); 745 default: return "?"; // unknown 746 // TODO: add "*" char*, "#" Class, "@" id, ":" SEL 747 // TODO: add "^"<type> indirection and "^^" double indirection 748 } 749 } 750 751 /** 752 * Returns: the `L_OBJC_METH_VAR_TYPE_` symbol containing the given 753 * type encoding. 754 */ 755 Symbol* getMethVarType(const(char)[] typeEncoding) 756 { 757 hasSymbols_ = true; 758 759 auto stringValue = methVarTypeTable.update(typeEncoding); 760 auto symbol = stringValue.value; 761 762 if (symbol) 763 return symbol; 764 765 __gshared size_t count = 0; 766 char[42] nameString; 767 const symbolName = format(nameString, "L_OBJC_METH_VAR_TYPE_%lu", count++); 768 symbol = getCString(typeEncoding, symbolName, Segments.Id.methtype); 769 770 stringValue.value = symbol; 771 outdata(symbol); 772 773 return symbol; 774 } 775 776 /// ditto 777 Symbol* getMethVarType(Type[] types ...) 778 { 779 string typeCode; 780 typeCode.reserve(types.length); 781 782 foreach (type; types) 783 typeCode ~= getTypeEncoding(type); 784 785 return getMethVarType(typeCode); 786 } 787 788 /// ditto 789 Symbol* getMethVarType(FuncDeclaration func) 790 { 791 Type[] types = [func.type.nextOf]; // return type first 792 793 if (func.parameters) 794 { 795 types.reserve(func.parameters.length); 796 797 foreach (e; *func.parameters) 798 types ~= e.type; 799 } 800 801 return getMethVarType(types); 802 } 803 804 /// Returns: the externally defined `__objc_empty_cache` symbol 805 Symbol* getEmptyCache() 806 { 807 return emptyCache = emptyCache ? emptyCache : getGlobal("__objc_empty_cache"); 808 } 809 810 /// Returns: the externally defined `__objc_empty_vtable` symbol 811 Symbol* getEmptyVTable() 812 { 813 return emptyVTable = emptyVTable ? emptyVTable : getGlobal("__objc_empty_vtable"); 814 } 815 816 /// Returns: the `L_OBJC_CLASS_NAME_` symbol for a class with the given name 817 Symbol* getClassNameRo(const(char)[] name) 818 { 819 hasSymbols_ = true; 820 821 auto stringValue = classNameTable.update(name); 822 auto symbol = stringValue.value; 823 824 __gshared size_t count = 0; 825 char[42] nameString; 826 const symbolName = format(nameString, "L_OBJC_CLASS_NAME_%lu", count++); 827 symbol = getCString(name, symbolName, Segments.Id.classname); 828 stringValue.value = symbol; 829 830 return symbol; 831 } 832 833 /// ditto 834 Symbol* getClassNameRo(const Identifier ident) 835 { 836 return getClassNameRo(ident.toString()); 837 } 838 839 Symbol* getIVarOffset(ClassDeclaration cd, VarDeclaration var, bool outputSymbol) 840 { 841 hasSymbols_ = true; 842 843 const className = cd.objc.identifier.toString; 844 const varName = var.ident.toString; 845 const name = "_OBJC_IVAR_$_" ~ className ~ '.' ~ varName; 846 847 auto stringValue = ivarOffsetTable.update(name); 848 auto symbol = stringValue.value; 849 850 if (!symbol) 851 { 852 symbol = getGlobal(name); 853 symbol.Sfl |= FLextern; 854 stringValue.value = symbol; 855 } 856 857 if (!outputSymbol) 858 return symbol; 859 860 auto dtb = DtBuilder(0); 861 dtb.size(var.offset); 862 863 symbol.Sdt = dtb.finish(); 864 symbol.Sseg = Segments[Segments.Id.ivar]; 865 symbol.Sfl &= ~FLextern; 866 867 outdata(symbol); 868 869 return symbol; 870 } 871 872 private Symbol* setMsgSendSymbol(string name)(tym_t ty = TYnfunc) 873 { 874 alias This = typeof(this); 875 enum fieldName = name[1 .. $]; 876 877 if (!__traits(getMember, This, fieldName)) 878 __traits(getMember, This, fieldName) = getGlobal(name, type_fake(ty)); 879 880 return __traits(getMember, This, fieldName); 881 } 882 } 883 884 private: 885 886 /** 887 * Functionality for outputting symbols for a specific Objective-C class 888 * declaration. 889 */ 890 struct ObjcClassDeclaration 891 { 892 /// Indicates what kind of class this is. 893 private enum Flags 894 { 895 /// Regular class. 896 regular = 0x00000, 897 898 /// Meta class. 899 meta = 0x00001, 900 901 /// Root class. A class without any base class. 902 root = 0x00002 903 } 904 905 /// The class declaration 906 ClassDeclaration classDeclaration; 907 908 /// `true` if this class is a metaclass. 909 bool isMeta; 910 911 this(ClassDeclaration classDeclaration, bool isMeta) 912 in 913 { 914 assert(classDeclaration !is null); 915 } 916 do 917 { 918 this.classDeclaration = classDeclaration; 919 this.isMeta = isMeta; 920 } 921 922 /** 923 * Outputs the class declaration to the object file. 924 * 925 * Returns: the exported symbol, that is, `_OBJC_METACLASS_$_` or 926 * `_OBJC_CLASS_$_` 927 */ 928 Symbol* toObjFile() 929 { 930 if (classDeclaration.objc.isExtern) 931 return null; // only a declaration for an externally-defined class 932 933 auto dtb = DtBuilder(0); 934 toDt(dtb); 935 936 auto symbol = Symbols.getClassName(this); 937 symbol.Sdt = dtb.finish(); 938 symbol.Sseg = Segments[Segments.Id.data]; 939 outdata(symbol); 940 941 return symbol; 942 } 943 944 private: 945 946 /** 947 * Outputs the class declaration to the object file. 948 * 949 * Params: 950 * dtb = the `DtBuilder` to output the class declaration to 951 */ 952 void toDt(ref DtBuilder dtb) 953 { 954 auto baseClassSymbol = classDeclaration.baseClass ? 955 Symbols.getClassName(classDeclaration.baseClass, isMeta) : null; 956 957 dtb.xoff(getMetaclass(), 0); // pointer to metaclass 958 dtb.xoffOrNull(baseClassSymbol); // pointer to base class 959 dtb.xoff(Symbols.getEmptyCache(), 0); 960 dtb.xoff(Symbols.getEmptyVTable(), 0); 961 dtb.xoff(getClassRo(), 0); 962 } 963 964 /// Returns: the name of the metaclass of this class declaration 965 Symbol* getMetaclass() 966 { 967 if (isMeta) 968 { 969 // metaclass: return root class's name 970 // (will be replaced with metaclass reference at load) 971 972 auto metaclassDeclaration = classDeclaration; 973 974 while (metaclassDeclaration.baseClass) 975 metaclassDeclaration = metaclassDeclaration.baseClass; 976 977 return Symbols.getClassName(metaclassDeclaration, true); 978 } 979 980 else 981 { 982 // regular class: return metaclass with the same name 983 return ObjcClassDeclaration(classDeclaration, true).toObjFile(); 984 } 985 } 986 987 /** 988 * Returns: the `l_OBJC_CLASS_RO_$_`/`l_OBJC_METACLASS_RO_$_` symbol for 989 * this class declaration 990 */ 991 Symbol* getClassRo() 992 { 993 auto dtb = DtBuilder(0); 994 995 dtb.dword(flags); 996 dtb.dword(instanceStart); 997 dtb.dword(instanceSize); 998 dtb.dword(0); // reserved 999 1000 dtb.size(0); // ivar layout 1001 dtb.xoff(Symbols.getClassNameRo(classDeclaration.ident), 0); // name of the class 1002 1003 dtb.xoffOrNull(getMethodList()); // instance method list 1004 dtb.xoffOrNull(getProtocolList()); // protocol list 1005 1006 if (isMeta) 1007 { 1008 dtb.size(0); // instance variable list 1009 dtb.size(0); // weak ivar layout 1010 dtb.size(0); // properties 1011 } 1012 1013 else 1014 { 1015 dtb.xoffOrNull(getIVarList()); // instance variable list 1016 dtb.size(0); // weak ivar layout 1017 dtb.xoffOrNull(getPropertyList()); // properties 1018 } 1019 1020 const prefix = isMeta ? "l_OBJC_METACLASS_RO_$_" : "l_OBJC_CLASS_RO_$_"; 1021 const symbolName = prefix ~ classDeclaration.objc.identifier.toString(); 1022 auto symbol = Symbols.getStatic(symbolName); 1023 1024 symbol.Sdt = dtb.finish(); 1025 symbol.Sseg = Segments[Segments.Id.const_]; 1026 outdata(symbol); 1027 1028 return symbol; 1029 } 1030 1031 /** 1032 * Returns method list for this class declaration. 1033 * 1034 * This is a list of all methods defined in this class declaration, i.e. 1035 * methods with a body. 1036 * 1037 * Returns: the symbol for the method list, `l_OBJC_$_CLASS_METHODS_` or 1038 * `l_OBJC_$_INSTANCE_METHODS_` 1039 */ 1040 Symbol* getMethodList() 1041 { 1042 /** 1043 * Returns the number of methods that should be added to the binary. 1044 * 1045 * Only methods with a body should be added. 1046 * 1047 * Params: 1048 * members = the members of the class declaration 1049 */ 1050 static int methodCount(Dsymbols* members) 1051 { 1052 int count; 1053 1054 members.foreachDsymbol((member) { 1055 const func = member.isFuncDeclaration; 1056 1057 if (func && func.fbody) 1058 count++; 1059 }); 1060 1061 return count; 1062 } 1063 1064 auto methods = isMeta ? classDeclaration.objc.metaclass.objc.methodList : 1065 classDeclaration.objc.methodList; 1066 1067 const count = methodCount(methods); 1068 1069 if (count == 0) 1070 return null; 1071 1072 auto dtb = DtBuilder(0); 1073 1074 dtb.dword(24); // _objc_method.sizeof 1075 dtb.dword(count); // method count 1076 1077 methods.foreachDsymbol((method) { 1078 auto func = method.isFuncDeclaration; 1079 1080 if (func && func.fbody) 1081 { 1082 assert(func.selector); 1083 dtb.xoff(func.selector.toNameSymbol(), 0); // method name 1084 dtb.xoff(Symbols.getMethVarType(func), 0); // method type string 1085 dtb.xoff(func.toSymbol(), 0); // function implementation 1086 } 1087 }); 1088 1089 const prefix = isMeta ? "l_OBJC_$_CLASS_METHODS_" : "l_OBJC_$_INSTANCE_METHODS_"; 1090 const symbolName = prefix ~ classDeclaration.objc.identifier.toString(); 1091 auto symbol = Symbols.getStatic(symbolName); 1092 1093 symbol.Sdt = dtb.finish(); 1094 symbol.Sseg = Segments[Segments.Id.const_]; 1095 1096 return symbol; 1097 } 1098 1099 Symbol* getProtocolList() 1100 { 1101 // protocols are not supported yet 1102 return null; 1103 } 1104 1105 Symbol* getIVarList() 1106 { 1107 if (isMeta || classDeclaration.fields.length == 0) 1108 return null; 1109 1110 auto dtb = DtBuilder(0); 1111 1112 dtb.dword(32); // entsize, _ivar_t.sizeof 1113 dtb.dword(cast(int) classDeclaration.fields.length); // ivar count 1114 1115 foreach (field; classDeclaration.fields) 1116 { 1117 auto var = field.isVarDeclaration; 1118 assert(var); 1119 assert((var.storage_class & STC.static_) == 0); 1120 1121 dtb.xoff(Symbols.getIVarOffset(classDeclaration, var, true), 0); // pointer to ivar offset 1122 dtb.xoff(Symbols.getMethVarName(var.ident), 0); // name 1123 dtb.xoff(Symbols.getMethVarType(var.type), 0); // type string 1124 dtb.dword(var.alignment); 1125 dtb.dword(cast(int) var.size(var.loc)); 1126 } 1127 1128 enum prefix = "l_OBJC_$_INSTANCE_VARIABLES_"; 1129 const symbolName = prefix ~ classDeclaration.objc.identifier.toString(); 1130 auto symbol = Symbols.getStatic(symbolName); 1131 1132 symbol.Sdt = dtb.finish(); 1133 symbol.Sseg = Segments[Segments.Id.const_]; 1134 1135 return symbol; 1136 } 1137 1138 Symbol* getPropertyList() 1139 { 1140 // properties are not supported yet 1141 return null; 1142 } 1143 1144 Symbol* getIVarOffset(VarDeclaration var) 1145 { 1146 if (var.toParent() is classDeclaration) 1147 return Symbols.getIVarOffset(classDeclaration, var, false); 1148 1149 else if (classDeclaration.baseClass) 1150 return ObjcClassDeclaration(classDeclaration.baseClass, false) 1151 .getIVarOffset(var); 1152 1153 else 1154 assert(false, "Trying to get the base class of a root class"); 1155 } 1156 1157 /** 1158 * Returns the flags for this class declaration. 1159 * 1160 * That is, if this is a regular class, a metaclass and/or a root class. 1161 * 1162 * Returns: the flags 1163 */ 1164 uint flags() const 1165 { 1166 uint flags = isMeta ? Flags.meta : Flags.regular; 1167 1168 if (classDeclaration.objc.isRootClass) 1169 flags |= Flags.root; 1170 1171 return flags; 1172 } 1173 1174 /** 1175 * Returns the offset of where an instance of this class starts. 1176 * 1177 * For a metaclass this is always `40`. For a class with no instance 1178 * variables this is the size of the class declaration. For a class with 1179 * instance variables it's the offset of the first instance variable. 1180 * 1181 * Returns: the instance start 1182 */ 1183 int instanceStart() 1184 { 1185 if (isMeta) 1186 return 40; 1187 1188 const start = cast(uint) classDeclaration.size(classDeclaration.loc); 1189 1190 if (!classDeclaration.members || classDeclaration.members.length == 0) 1191 return start; 1192 1193 foreach (member; *classDeclaration.members) 1194 { 1195 auto var = member.isVarDeclaration; 1196 1197 if (var && var.isField) 1198 return var.offset; 1199 } 1200 1201 return start; 1202 } 1203 1204 /// Returns: the size of an instance of this class 1205 int instanceSize() 1206 { 1207 return isMeta ? 40 : cast(int) classDeclaration.size(classDeclaration.loc); 1208 } 1209 } 1210 1211 /* 1212 * Formats the given arguments into the given buffer. 1213 * 1214 * Convenience wrapper around `snprintf`. 1215 * 1216 * Params: 1217 * bufLength = length of the buffer 1218 * buffer = the buffer where to store the result 1219 * format = the format string 1220 * args = the arguments to format 1221 * 1222 * Returns: the formatted result, a slice of the given buffer 1223 */ 1224 char[] format(size_t bufLength, Args...)(return ref char[bufLength] buffer, 1225 const(char)* format, const Args args) 1226 { 1227 auto length = snprintf(buffer.ptr, buffer.length, format, args); 1228 1229 assert(length >= 0, "An output error occurred"); 1230 assert(length < buffer.length, "Output was truncated"); 1231 1232 return buffer[0 .. length]; 1233 } 1234 1235 /// Returns: the symbol of the given selector 1236 Symbol* toNameSymbol(const ObjcSelector* selector) 1237 { 1238 return Symbols.getMethVarName(selector.toString()); 1239 } 1240 1241 /** 1242 * Adds a reference to the given `symbol` or null if the symbol is null. 1243 * 1244 * Params: 1245 * dtb = the dt builder to add the symbol to 1246 * symbol = the symbol to add 1247 */ 1248 void xoffOrNull(ref DtBuilder dtb, Symbol* symbol) 1249 { 1250 if (symbol) 1251 dtb.xoff(symbol, 0); 1252 else 1253 dtb.size(0); 1254 } 1255 1256 /** 1257 * Converts the given D string to a null terminated C string. 1258 * 1259 * Asserts if `str` is longer than `maxLength`, with assertions enabled. With 1260 * assertions disabled it will truncate the result to `maxLength`. 1261 * 1262 * Params: 1263 * maxLength = the max length of `str` 1264 * str = the string to convert 1265 * buf = the buffer where to allocate the result. By default this will be 1266 * allocated in the caller scope using `alloca`. If the buffer is created 1267 * by the callee it needs to be able to fit at least `str.length + 1` bytes 1268 * 1269 * Returns: the given string converted to a C string, a slice of `str` or the 1270 * given buffer `buffer` 1271 */ 1272 const(char)* toStringz(size_t maxLength = 4095)(in const(char)[] str, 1273 scope return void[] buffer = alloca(maxLength + 1)[0 .. maxLength + 1]) pure 1274 in 1275 { 1276 assert(maxLength >= str.length); 1277 } 1278 out(result) 1279 { 1280 assert(str.length == result.strlen); 1281 } 1282 do 1283 { 1284 if (str.length == 0) 1285 return "".ptr; 1286 1287 const maxLength = buffer.length - 1; 1288 const len = str.length > maxLength ? maxLength : str.length; 1289 auto buf = cast(char[]) buffer[0 .. len + 1]; 1290 buf[0 .. len] = str[0 .. len]; 1291 buf[len] = '\0'; 1292 1293 return cast(const(char)*) buf.ptr; 1294 }