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