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