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