1 /** 2 * Interfacing with Objective-C. 3 * 4 * Specification: $(LINK2 https://dlang.org/spec/objc_interface.html, Interfacing to Objective-C) 5 * 6 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved 7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/objc.d, _objc.d) 10 * Documentation: https://dlang.org/phobos/dmd_objc.html 11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/objc.d 12 */ 13 14 module dmd.objc; 15 16 import dmd.aggregate; 17 import dmd.arraytypes; 18 import dmd.attrib; 19 import dmd.cond; 20 import dmd.dclass; 21 import dmd.declaration; 22 import dmd.denum; 23 import dmd.dmangle; 24 import dmd.dmodule; 25 import dmd.dscope; 26 import dmd.dstruct; 27 import dmd.dsymbol; 28 import dmd.dsymbolsem; 29 import dmd.errors; 30 import dmd.expression; 31 import dmd.expressionsem; 32 import dmd.func; 33 import dmd.globals; 34 import dmd.gluelayer; 35 import dmd.hdrgen; 36 import dmd.id; 37 import dmd.identifier; 38 import dmd.mtype; 39 import dmd.root.array; 40 import dmd.root.outbuffer; 41 import dmd.root.stringtable; 42 import dmd.target; 43 import dmd.tokens; 44 45 struct ObjcSelector 46 { 47 // MARK: Selector 48 private __gshared StringTable!(ObjcSelector*) stringtable; 49 private __gshared int incnum = 0; 50 const(char)* stringvalue; 51 size_t stringlen; 52 size_t paramCount; 53 54 extern (C++) static void _init() 55 { 56 stringtable._init(); 57 } 58 59 extern (D) this(const(char)* sv, size_t len, size_t pcount) 60 { 61 stringvalue = sv; 62 stringlen = len; 63 paramCount = pcount; 64 } 65 66 extern (D) static ObjcSelector* lookup(const(char)* s) 67 { 68 size_t len = 0; 69 size_t pcount = 0; 70 const(char)* i = s; 71 while (*i != 0) 72 { 73 ++len; 74 if (*i == ':') 75 ++pcount; 76 ++i; 77 } 78 return lookup(s, len, pcount); 79 } 80 81 extern (D) static ObjcSelector* lookup(const(char)* s, size_t len, size_t pcount) 82 { 83 auto sv = stringtable.update(s, len); 84 ObjcSelector* sel = sv.value; 85 if (!sel) 86 { 87 sel = new ObjcSelector(sv.toDchars(), len, pcount); 88 sv.value = sel; 89 } 90 return sel; 91 } 92 93 extern (C++) static ObjcSelector* create(FuncDeclaration fdecl) 94 { 95 OutBuffer buf; 96 TypeFunction ftype = cast(TypeFunction)fdecl.type; 97 const id = fdecl.ident.toString(); 98 const nparams = ftype.parameterList.length; 99 // Special case: property setter 100 if (ftype.isproperty && nparams == 1) 101 { 102 // rewrite "identifier" as "setIdentifier" 103 char firstChar = id[0]; 104 if (firstChar >= 'a' && firstChar <= 'z') 105 firstChar = cast(char)(firstChar - 'a' + 'A'); 106 buf.writestring("set"); 107 buf.writeByte(firstChar); 108 buf.write(id[1 .. id.length - 1]); 109 buf.writeByte(':'); 110 goto Lcomplete; 111 } 112 // write identifier in selector 113 buf.write(id[]); 114 // add mangled type and colon for each parameter 115 if (nparams) 116 { 117 buf.writeByte('_'); 118 foreach (i, fparam; ftype.parameterList) 119 { 120 mangleToBuffer(fparam.type, &buf); 121 buf.writeByte(':'); 122 } 123 } 124 Lcomplete: 125 buf.writeByte('\0'); 126 // the slice is not expected to include a terminating 0 127 return lookup(cast(const(char)*)buf[].ptr, buf.length - 1, nparams); 128 } 129 130 extern (D) const(char)[] toString() const pure 131 { 132 return stringvalue[0 .. stringlen]; 133 } 134 } 135 136 private __gshared Objc _objc; 137 138 Objc objc() 139 { 140 return _objc; 141 } 142 143 144 /** 145 * Contains all data for a class declaration that is needed for the Objective-C 146 * integration. 147 */ 148 extern (C++) struct ObjcClassDeclaration 149 { 150 /// `true` if this class is a metaclass. 151 bool isMeta = false; 152 153 /// `true` if this class is externally defined. 154 bool isExtern = false; 155 156 /// Name of this class. 157 Identifier identifier; 158 159 /// The class declaration this belongs to. 160 ClassDeclaration classDeclaration; 161 162 /// The metaclass of this class. 163 ClassDeclaration metaclass; 164 165 /// List of non-inherited methods. 166 FuncDeclaration[] methodList; 167 168 extern (D) this(ClassDeclaration classDeclaration) 169 { 170 this.classDeclaration = classDeclaration; 171 } 172 173 bool isRootClass() const 174 { 175 return classDeclaration.classKind == ClassKind.objc && 176 !metaclass && 177 !classDeclaration.baseClass; 178 } 179 } 180 181 /** 182 * Contains all data for a function declaration that is needed for the 183 * Objective-C integration. 184 */ 185 extern (C++) struct ObjcFuncDeclaration 186 { 187 /// The method selector (member functions only). 188 ObjcSelector* selector; 189 190 /// The implicit selector parameter. 191 VarDeclaration selectorParameter; 192 193 /// `true` if this function declaration is declared optional. 194 bool isOptional; 195 } 196 197 // Should be an interface 198 extern(C++) abstract class Objc 199 { 200 static void _init() 201 { 202 if (target.objc.supported) 203 _objc = new Supported; 204 else 205 _objc = new Unsupported; 206 } 207 208 /** 209 * Deinitializes the global state of the compiler. 210 * 211 * This can be used to restore the state set by `_init` to its original 212 * state. 213 */ 214 static void deinitialize() 215 { 216 _objc = _objc.init; 217 } 218 219 abstract void setObjc(ClassDeclaration cd); 220 abstract void setObjc(InterfaceDeclaration); 221 222 /** 223 * Returns a pretty textual representation of the given class declaration. 224 * 225 * Params: 226 * classDeclaration = the class declaration to return the textual representation for 227 * qualifyTypes = `true` if types should be qualified in the result 228 * 229 * Returns: the textual representation 230 */ 231 abstract const(char)* toPrettyChars(ClassDeclaration classDeclaration, bool qualifyTypes) const; 232 233 abstract void setSelector(FuncDeclaration, Scope* sc); 234 abstract void validateSelector(FuncDeclaration fd); 235 abstract void checkLinkage(FuncDeclaration fd); 236 237 /** 238 * Returns `true` if the given function declaration is virtual. 239 * 240 * Function declarations with Objective-C linkage and which are static or 241 * final are considered virtual. 242 * 243 * Params: 244 * fd = the function declaration to check if it's virtual 245 * 246 * Returns: `true` if the given function declaration is virtual 247 */ 248 abstract bool isVirtual(const FuncDeclaration fd) const; 249 250 /** 251 * Marks the given function declaration as optional. 252 * 253 * A function declaration is considered optional if it's annotated with the 254 * UDA: `@(core.attribute.optional)`. Only function declarations inside 255 * interface declarations and with Objective-C linkage can be declared as 256 * optional. 257 * 258 * Params: 259 * functionDeclaration = the function declaration to be set as optional 260 * sc = the scope from the semantic phase 261 */ 262 abstract void setAsOptional(FuncDeclaration functionDeclaration, Scope* sc) const; 263 264 /** 265 * Validates function declarations declared optional. 266 * 267 * Params: 268 * functionDeclaration = the function declaration to validate 269 */ 270 abstract void validateOptional(FuncDeclaration functionDeclaration) const; 271 272 /** 273 * Gets the parent of the given function declaration. 274 * 275 * Handles Objective-C static member functions, which are virtual functions 276 * of the metaclass, by returning the parent class declaration to the 277 * metaclass. 278 * 279 * Params: 280 * fd = the function declaration to get the parent of 281 * cd = the current parent, i.e. the class declaration the given function 282 * declaration belongs to 283 * 284 * Returns: the parent 285 */ 286 abstract ClassDeclaration getParent(FuncDeclaration fd, 287 ClassDeclaration cd) const; 288 289 /** 290 * Adds the given function to the list of Objective-C methods. 291 * 292 * This list will later be used output the necessary Objective-C module info. 293 * 294 * Params: 295 * fd = the function declaration to be added to the list 296 * cd = the class declaration the function belongs to 297 */ 298 abstract void addToClassMethodList(FuncDeclaration fd, 299 ClassDeclaration cd) const; 300 301 /** 302 * Returns the `this` pointer of the given function declaration. 303 * 304 * This is only used for class/static methods. For instance methods, no 305 * Objective-C specialization is necessary. 306 * 307 * Params: 308 * funcDeclaration = the function declaration to get the `this` pointer for 309 * 310 * Returns: the `this` pointer of the given function declaration, or `null` 311 * if the given function declaration is not an Objective-C method. 312 */ 313 abstract inout(AggregateDeclaration) isThis(inout FuncDeclaration funcDeclaration) const; 314 315 /** 316 * Creates the selector parameter for the given function declaration. 317 * 318 * Objective-C methods has an extra hidden parameter that comes after the 319 * `this` parameter. The selector parameter is of the Objective-C type `SEL` 320 * and contains the selector which this method was called with. 321 * 322 * Params: 323 * fd = the function declaration to create the parameter for 324 * sc = the scope from the semantic phase 325 * 326 * Returns: the newly created selector parameter or `null` for 327 * non-Objective-C functions 328 */ 329 abstract VarDeclaration createSelectorParameter(FuncDeclaration fd, Scope* sc) const; 330 331 /** 332 * Creates and sets the metaclass on the given class/interface declaration. 333 * 334 * Will only be performed on regular Objective-C classes, not on metaclasses. 335 * 336 * Params: 337 * classDeclaration = the class/interface declaration to set the metaclass on 338 */ 339 abstract void setMetaclass(InterfaceDeclaration interfaceDeclaration, Scope* sc) const; 340 341 /// ditto 342 abstract void setMetaclass(ClassDeclaration classDeclaration, Scope* sc) const; 343 344 /** 345 * Returns Objective-C runtime metaclass of the given class declaration. 346 * 347 * `ClassDeclaration.ObjcClassDeclaration.metaclass` contains the metaclass 348 * from the semantic point of view. This function returns the metaclass from 349 * the Objective-C runtime's point of view. Here, the metaclass of a 350 * metaclass is the root metaclass, not `null`. The root metaclass's 351 * metaclass is itself. 352 * 353 * Params: 354 * classDeclaration = The class declaration to return the metaclass of 355 * 356 * Returns: the Objective-C runtime metaclass of the given class declaration 357 */ 358 abstract ClassDeclaration getRuntimeMetaclass(ClassDeclaration classDeclaration) const; 359 360 /// 361 abstract void addSymbols(AttribDeclaration attribDeclaration, 362 ClassDeclarations* classes, ClassDeclarations* categories) const; 363 364 /// 365 abstract void addSymbols(ClassDeclaration classDeclaration, 366 ClassDeclarations* classes, ClassDeclarations* categories) const; 367 368 /** 369 * Issues a compile time error if the `.offsetof`/`.tupleof` property is 370 * used on a field of an Objective-C class. 371 * 372 * To solve the fragile base class problem in Objective-C, fields have a 373 * dynamic offset instead of a static offset. The compiler outputs a 374 * statically known offset which later the dynamic loader can update, if 375 * necessary, when the application is loaded. Due to this behavior it 376 * doesn't make sense to be able to get the offset of a field at compile 377 * time, because this offset might not actually be the same at runtime. 378 * 379 * To get the offset of a field that is correct at runtime, functionality 380 * from the Objective-C runtime can be used instead. 381 * 382 * Params: 383 * expression = the `.offsetof`/`.tupleof` expression 384 * aggregateDeclaration = the aggregate declaration the field of the 385 * `.offsetof`/`.tupleof` expression belongs to 386 * type = the type of the receiver of the `.tupleof` expression 387 * 388 * See_Also: 389 * $(LINK2 https://en.wikipedia.org/wiki/Fragile_binary_interface_problem, 390 * Fragile Binary Interface Problem) 391 * 392 * See_Also: 393 * $(LINK2 https://developer.apple.com/documentation/objectivec/objective_c_runtime, 394 * Objective-C Runtime) 395 */ 396 abstract void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const; 397 398 /// ditto 399 abstract void checkTupleof(Expression expression, TypeClass type) const; 400 } 401 402 extern(C++) private final class Unsupported : Objc 403 { 404 extern(D) final this() 405 { 406 ObjcGlue.initialize(); 407 } 408 409 override void setObjc(ClassDeclaration cd) 410 { 411 cd.error("Objective-C classes not supported"); 412 } 413 414 override void setObjc(InterfaceDeclaration id) 415 { 416 id.error("Objective-C interfaces not supported"); 417 } 418 419 override const(char)* toPrettyChars(ClassDeclaration, bool qualifyTypes) const 420 { 421 assert(0, "Should never be called when Objective-C is not supported"); 422 } 423 424 override void setSelector(FuncDeclaration, Scope*) 425 { 426 // noop 427 } 428 429 override void validateSelector(FuncDeclaration) 430 { 431 // noop 432 } 433 434 override void checkLinkage(FuncDeclaration) 435 { 436 // noop 437 } 438 439 override bool isVirtual(const FuncDeclaration) const 440 { 441 assert(0, "Should never be called when Objective-C is not supported"); 442 } 443 444 override void setAsOptional(FuncDeclaration, Scope*) const 445 { 446 // noop 447 } 448 449 override void validateOptional(FuncDeclaration) const 450 { 451 // noop 452 } 453 454 override ClassDeclaration getParent(FuncDeclaration, ClassDeclaration cd) const 455 { 456 return cd; 457 } 458 459 override void addToClassMethodList(FuncDeclaration, ClassDeclaration) const 460 { 461 // noop 462 } 463 464 override inout(AggregateDeclaration) isThis(inout FuncDeclaration funcDeclaration) const 465 { 466 return null; 467 } 468 469 override VarDeclaration createSelectorParameter(FuncDeclaration, Scope*) const 470 { 471 return null; 472 } 473 474 override void setMetaclass(InterfaceDeclaration, Scope*) const 475 { 476 // noop 477 } 478 479 override void setMetaclass(ClassDeclaration, Scope*) const 480 { 481 // noop 482 } 483 484 override ClassDeclaration getRuntimeMetaclass(ClassDeclaration classDeclaration) const 485 { 486 assert(0, "Should never be called when Objective-C is not supported"); 487 } 488 489 override void addSymbols(AttribDeclaration attribDeclaration, 490 ClassDeclarations* classes, ClassDeclarations* categories) const 491 { 492 // noop 493 } 494 495 override void addSymbols(ClassDeclaration classDeclaration, 496 ClassDeclarations* classes, ClassDeclarations* categories) const 497 { 498 // noop 499 } 500 501 override void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const 502 { 503 // noop 504 } 505 506 override void checkTupleof(Expression expression, TypeClass type) const 507 { 508 // noop 509 } 510 } 511 512 extern(C++) private final class Supported : Objc 513 { 514 extern(D) final this() 515 { 516 VersionCondition.addPredefinedGlobalIdent("D_ObjectiveC"); 517 518 ObjcGlue.initialize(); 519 ObjcSelector._init(); 520 } 521 522 override void setObjc(ClassDeclaration cd) 523 { 524 cd.classKind = ClassKind.objc; 525 cd.objc.isExtern = (cd.storage_class & STC.extern_) > 0; 526 } 527 528 override void setObjc(InterfaceDeclaration id) 529 { 530 id.classKind = ClassKind.objc; 531 id.objc.isExtern = true; 532 } 533 534 override const(char)* toPrettyChars(ClassDeclaration cd, bool qualifyTypes) const 535 { 536 return cd.parent.toPrettyChars(qualifyTypes); 537 } 538 539 override void setSelector(FuncDeclaration fd, Scope* sc) 540 { 541 foreachUda(fd, sc, (e) { 542 if (e.op != TOK.structLiteral) 543 return 0; 544 545 auto literal = cast(StructLiteralExp) e; 546 assert(literal.sd); 547 548 if (!isCoreUda(literal.sd, Id.udaSelector)) 549 return 0; 550 551 if (fd.objc.selector) 552 { 553 fd.error("can only have one Objective-C selector per method"); 554 return 1; 555 } 556 557 assert(literal.elements.dim == 1); 558 auto se = (*literal.elements)[0].toStringExp(); 559 assert(se); 560 561 fd.objc.selector = ObjcSelector.lookup(se.toUTF8(sc).peekString().ptr); 562 563 return 0; 564 }); 565 } 566 567 override void validateSelector(FuncDeclaration fd) 568 { 569 if (!fd.objc.selector) 570 return; 571 TypeFunction tf = cast(TypeFunction)fd.type; 572 if (fd.objc.selector.paramCount != tf.parameterList.parameters.dim) 573 fd.error("number of colons in Objective-C selector must match number of parameters"); 574 if (fd.parent && fd.parent.isTemplateInstance()) 575 fd.error("template cannot have an Objective-C selector attached"); 576 } 577 578 override void checkLinkage(FuncDeclaration fd) 579 { 580 if (fd.linkage != LINK.objc && fd.objc.selector) 581 fd.error("must have Objective-C linkage to attach a selector"); 582 } 583 584 override bool isVirtual(const FuncDeclaration fd) const 585 in 586 { 587 assert(fd.selector); 588 assert(fd.isMember); 589 } 590 do 591 { 592 if (fd.toParent.isInterfaceDeclaration && fd.isFinal) 593 return false; 594 595 // * final member functions are kept virtual with Objective-C linkage 596 // because the Objective-C runtime always use dynamic dispatch. 597 // * static member functions are kept virtual too, as they represent 598 // methods of the metaclass. 599 with (fd.visibility) 600 return !(kind == Visibility.Kind.private_ || kind == Visibility.Kind.package_); 601 } 602 603 override void setAsOptional(FuncDeclaration fd, Scope* sc) const 604 { 605 const count = declaredAsOptionalCount(fd, sc); 606 fd.objc.isOptional = count > 0; 607 608 if (count > 1) 609 fd.error("can only declare a function as optional once"); 610 } 611 612 /// Returns: the number of times `fd` has been declared as optional. 613 private int declaredAsOptionalCount(FuncDeclaration fd , Scope* sc) const 614 { 615 int count; 616 617 foreachUda(fd, sc, (e) { 618 if (e.op != TOK.type) 619 return 0; 620 621 auto typeExp = cast(TypeExp) e; 622 623 if (typeExp.type.ty != Tenum) 624 return 0; 625 626 auto typeEnum = cast(TypeEnum) typeExp.type; 627 628 if (isCoreUda(typeEnum.sym, Id.udaOptional)) 629 count++; 630 631 return 0; 632 }); 633 634 return count; 635 } 636 637 override void validateOptional(FuncDeclaration fd) const 638 { 639 if (!fd.objc.isOptional) 640 return; 641 642 if (fd.linkage != LINK.objc) 643 { 644 fd.error("only functions with Objective-C linkage can be declared as optional"); 645 646 const linkage = linkageToString(fd.linkage); 647 648 errorSupplemental(fd.loc, "function is declared with %.*s linkage", 649 cast(uint) linkage.length, linkage.ptr); 650 } 651 652 auto parent = fd.parent; 653 654 if (parent && parent.isTemplateInstance()) 655 { 656 fd.error("template cannot be optional"); 657 parent = parent.parent; 658 assert(parent); 659 } 660 661 if (parent && !parent.isInterfaceDeclaration()) 662 { 663 fd.error("only functions declared inside interfaces can be optional"); 664 errorSupplemental(fd.loc, "function is declared inside %s", fd.parent.kind); 665 } 666 } 667 668 override ClassDeclaration getParent(FuncDeclaration fd, ClassDeclaration cd) const 669 out(metaclass) 670 { 671 assert(metaclass); 672 } 673 do 674 { 675 if (cd.classKind == ClassKind.objc && fd.isStatic && !cd.objc.isMeta) 676 return cd.objc.metaclass; 677 else 678 return cd; 679 } 680 681 override void addToClassMethodList(FuncDeclaration fd, ClassDeclaration cd) const 682 in 683 { 684 assert(fd.parent.isClassDeclaration); 685 } 686 do 687 { 688 if (cd.classKind != ClassKind.objc) 689 return; 690 691 if (!fd.objc.selector) 692 return; 693 694 assert(fd.isStatic ? cd.objc.isMeta : !cd.objc.isMeta); 695 696 cd.objc.methodList ~= fd; 697 } 698 699 override inout(AggregateDeclaration) isThis(inout FuncDeclaration funcDeclaration) const 700 { 701 with(funcDeclaration) 702 { 703 if (!objc.selector) 704 return null; 705 706 // Use Objective-C class object as 'this' 707 auto cd = isMember2().isClassDeclaration(); 708 709 if (cd.classKind == ClassKind.objc) 710 { 711 if (!cd.objc.isMeta) 712 return cd.objc.metaclass; 713 } 714 715 return null; 716 } 717 } 718 719 override VarDeclaration createSelectorParameter(FuncDeclaration fd, Scope* sc) const 720 in 721 { 722 assert(fd.selectorParameter is null); 723 } 724 do 725 { 726 if (!fd.objc.selector) 727 return null; 728 729 auto ident = Identifier.generateAnonymousId("_cmd"); 730 auto var = new VarDeclaration(fd.loc, Type.tvoidptr, ident, null); 731 var.storage_class |= STC.parameter; 732 var.dsymbolSemantic(sc); 733 if (!sc.insert(var)) 734 assert(false); 735 var.parent = fd; 736 737 return var; 738 } 739 740 override void setMetaclass(InterfaceDeclaration interfaceDeclaration, Scope* sc) const 741 { 742 auto newMetaclass(Loc loc, BaseClasses* metaBases) 743 { 744 auto ident = createMetaclassIdentifier(interfaceDeclaration); 745 return new InterfaceDeclaration(loc, ident, metaBases); 746 } 747 748 .setMetaclass!newMetaclass(interfaceDeclaration, sc); 749 } 750 751 override void setMetaclass(ClassDeclaration classDeclaration, Scope* sc) const 752 { 753 auto newMetaclass(Loc loc, BaseClasses* metaBases) 754 { 755 auto ident = createMetaclassIdentifier(classDeclaration); 756 return new ClassDeclaration(loc, ident, metaBases, new Dsymbols(), 0); 757 } 758 759 .setMetaclass!newMetaclass(classDeclaration, sc); 760 } 761 762 override ClassDeclaration getRuntimeMetaclass(ClassDeclaration classDeclaration) const 763 { 764 if (!classDeclaration.objc.metaclass && classDeclaration.objc.isMeta) 765 { 766 if (classDeclaration.baseClass) 767 return getRuntimeMetaclass(classDeclaration.baseClass); 768 else 769 return classDeclaration; 770 } 771 else 772 return classDeclaration.objc.metaclass; 773 } 774 775 override void addSymbols(AttribDeclaration attribDeclaration, 776 ClassDeclarations* classes, ClassDeclarations* categories) const 777 { 778 auto symbols = attribDeclaration.include(null); 779 780 if (!symbols) 781 return; 782 783 foreach (symbol; *symbols) 784 symbol.addObjcSymbols(classes, categories); 785 } 786 787 override void addSymbols(ClassDeclaration classDeclaration, 788 ClassDeclarations* classes, ClassDeclarations* categories) const 789 { 790 with (classDeclaration) 791 if (classKind == ClassKind.objc && !objc.isExtern && !objc.isMeta) 792 classes.push(classDeclaration); 793 } 794 795 override void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const 796 { 797 if (aggregateDeclaration.classKind != ClassKind.objc) 798 return; 799 800 enum errorMessage = "no property `offsetof` for member `%s` of type " ~ 801 "`%s`"; 802 803 enum supplementalMessage = "`offsetof` is not available for members " ~ 804 "of Objective-C classes. Please use the Objective-C runtime instead"; 805 806 expression.error(errorMessage, expression.toChars(), 807 expression.type.toChars()); 808 expression.errorSupplemental(supplementalMessage); 809 } 810 811 override void checkTupleof(Expression expression, TypeClass type) const 812 { 813 if (type.sym.classKind != ClassKind.objc) 814 return; 815 816 expression.error("no property `tupleof` for type `%s`", type.toChars()); 817 expression.errorSupplemental("`tupleof` is not available for members " ~ 818 "of Objective-C classes. Please use the Objective-C runtime instead"); 819 } 820 821 extern(D) private: 822 823 /** 824 * Returns `true` if the given symbol is a symbol declared in 825 * `core.attribute` and has the given identifier. 826 * 827 * This is used to determine if a symbol is a UDA declared in 828 * `core.attribute`. 829 * 830 * Params: 831 * sd = the symbol to check 832 * ident = the name of the expected UDA 833 */ 834 bool isCoreUda(ScopeDsymbol sd, Identifier ident) const 835 { 836 if (sd.ident != ident || !sd.parent) 837 return false; 838 839 auto _module = sd.parent.isModule(); 840 return _module && _module.isCoreModule(Id.attribute); 841 } 842 843 /** 844 * Iterates the UDAs attached to the given function declaration. 845 * 846 * If `dg` returns `!= 0`, it will stop the iteration and return that 847 * value, otherwise it will return 0. 848 * 849 * Params: 850 * fd = the function declaration to get the UDAs from 851 * dg = called once for each UDA. If `dg` returns `!= 0`, it will stop the 852 * iteration and return that value, otherwise it will return `0`. 853 */ 854 int foreachUda(FuncDeclaration fd, Scope* sc, int delegate(Expression) dg) const 855 { 856 if (!fd.userAttribDecl) 857 return 0; 858 859 auto udas = fd.userAttribDecl.getAttributes(); 860 arrayExpressionSemantic(udas, sc, true); 861 862 return udas.each!((uda) { 863 if (uda.op != TOK.tuple) 864 return 0; 865 866 auto exps = (cast(TupleExp) uda).exps; 867 868 return exps.each!((e) { 869 assert(e); 870 871 if (auto result = dg(e)) 872 return result; 873 874 return 0; 875 }); 876 }); 877 } 878 } 879 880 /* 881 * Creates and sets the metaclass on the given class/interface declaration. 882 * 883 * Will only be performed on regular Objective-C classes, not on metaclasses. 884 * 885 * Params: 886 * newMetaclass = a function that returns the metaclass to set. This should 887 * return the same type as `T`. 888 * classDeclaration = the class/interface declaration to set the metaclass on 889 */ 890 private void setMetaclass(alias newMetaclass, T)(T classDeclaration, Scope* sc) 891 if (is(T == ClassDeclaration) || is(T == InterfaceDeclaration)) 892 { 893 static if (is(T == ClassDeclaration)) 894 enum errorType = "class"; 895 else 896 enum errorType = "interface"; 897 898 with (classDeclaration) 899 { 900 if (classKind != ClassKind.objc || objc.isMeta || objc.metaclass) 901 return; 902 903 if (!objc.identifier) 904 objc.identifier = classDeclaration.ident; 905 906 auto metaBases = new BaseClasses(); 907 908 foreach (base ; baseclasses.opSlice) 909 { 910 auto baseCd = base.sym; 911 assert(baseCd); 912 913 if (baseCd.classKind == ClassKind.objc) 914 { 915 assert(baseCd.objc.metaclass); 916 assert(baseCd.objc.metaclass.objc.isMeta); 917 assert(baseCd.objc.metaclass.type.ty == Tclass); 918 919 auto metaBase = new BaseClass(baseCd.objc.metaclass.type); 920 metaBase.sym = baseCd.objc.metaclass; 921 metaBases.push(metaBase); 922 } 923 else 924 { 925 error("base " ~ errorType ~ " for an Objective-C " ~ 926 errorType ~ " must be `extern (Objective-C)`"); 927 } 928 } 929 930 objc.metaclass = newMetaclass(loc, metaBases); 931 objc.metaclass.storage_class |= STC.static_; 932 objc.metaclass.classKind = ClassKind.objc; 933 objc.metaclass.objc.isMeta = true; 934 objc.metaclass.objc.isExtern = objc.isExtern; 935 objc.metaclass.objc.identifier = objc.identifier; 936 937 if (baseClass) 938 objc.metaclass.baseClass = baseClass.objc.metaclass; 939 940 members.push(objc.metaclass); 941 objc.metaclass.addMember(sc, classDeclaration); 942 943 objc.metaclass.members = new Dsymbols(); 944 objc.metaclass.dsymbolSemantic(sc); 945 } 946 } 947 948 private Identifier createMetaclassIdentifier(ClassDeclaration classDeclaration) 949 { 950 const name = "class_" ~ classDeclaration.ident.toString ~ "_Meta"; 951 return Identifier.generateAnonymousId(name); 952 }