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