2 * Interfacing with Objective-C.
4 * Specification: $(LINK2 https://dlang.org/spec/objc_interface.html, Interfacing to Objective-C)
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
17 import dmd
.arraytypes
;
22 import dmd
.declaration
;
29 import dmd
.dsymbolsem
;
31 import dmd
.expression
;
32 import dmd
.expressionsem
;
38 import dmd
.identifier
;
41 import dmd
.root
.array
;
42 import dmd
.common
.outbuffer
;
43 import dmd
.root
.stringtable
;
50 private __gshared StringTable
!(ObjcSelector
*) stringtable
;
51 private __gshared
int incnum
= 0;
52 const(char)* stringvalue
;
56 extern (C
++) static void _init()
61 extern (D
) this(const(char)* sv
, size_t len
, size_t pcount
) @safe
68 extern (D
) static ObjcSelector
* lookup(const(char)* s
)
80 return lookup(s
, len
, pcount
);
83 extern (D
) static ObjcSelector
* lookup(const(char)* s
, size_t len
, size_t pcount
)
85 auto sv
= stringtable
.update(s
, len
);
86 ObjcSelector
* sel
= sv
.value
;
89 sel
= new ObjcSelector(sv
.toDchars(), len
, pcount
);
95 extern (C
++) static ObjcSelector
* create(FuncDeclaration fdecl
)
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)
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]);
114 // write identifier in selector
116 // add mangled type and colon for each parameter
120 foreach (i
, fparam
; ftype
.parameterList
)
122 mangleToBuffer(fparam
.type
, buf
);
128 // the slice is not expected to include a terminating 0
129 return lookup(cast(const(char)*)buf
[].ptr
, buf
.length
- 1, nparams
);
132 extern (D
) const(char)[] toString() const pure
134 return stringvalue
[0 .. stringlen
];
138 private __gshared Objc _objc
;
147 * Contains all data for a class declaration that is needed for the Objective-C
150 extern (C
++) struct ObjcClassDeclaration
152 /// `true` if this class is a metaclass.
155 /// `true` if this class is externally defined.
156 bool isExtern
= false;
158 /// Name of this class.
159 Identifier identifier
;
161 /// The class declaration this belongs to.
162 ClassDeclaration classDeclaration
;
164 /// The metaclass of this class.
165 ClassDeclaration metaclass
;
167 /// List of non-inherited methods.
168 FuncDeclaration
[] methodList
;
170 extern (D
) this(ClassDeclaration classDeclaration
) @safe
172 this.classDeclaration
= classDeclaration
;
175 bool isRootClass() const @safe
177 return classDeclaration
.classKind
== ClassKind
.objc
&&
179 !classDeclaration
.baseClass
;
184 * Contains all data for a function declaration that is needed for the
185 * Objective-C integration.
187 extern (C
++) struct ObjcFuncDeclaration
189 /// The method selector (member functions only).
190 ObjcSelector
* selector
;
192 /// The implicit selector parameter.
193 VarDeclaration selectorParameter
;
195 /// `true` if this function declaration is declared optional.
199 // Should be an interface
200 extern(C
++) abstract class Objc
204 if (target
.objc
.supported
)
205 _objc
= new Supported
;
207 _objc
= new Unsupported
;
211 * Deinitializes the global state of the compiler.
213 * This can be used to restore the state set by `_init` to its original
216 static void deinitialize()
221 abstract void setObjc(ClassDeclaration cd
);
222 abstract void setObjc(InterfaceDeclaration
);
225 * Returns a pretty textual representation of the given class declaration.
228 * classDeclaration = the class declaration to return the textual representation for
229 * qualifyTypes = `true` if types should be qualified in the result
231 * Returns: the textual representation
233 abstract const(char)* toPrettyChars(ClassDeclaration classDeclaration
, bool qualifyTypes
) const;
235 abstract void setSelector(FuncDeclaration
, Scope
* sc
);
236 abstract void validateSelector(FuncDeclaration fd
);
237 abstract void checkLinkage(FuncDeclaration fd
);
240 * Returns `true` if the given function declaration is virtual.
242 * Function declarations with Objective-C linkage and which are static or
243 * final are considered virtual.
246 * fd = the function declaration to check if it's virtual
248 * Returns: `true` if the given function declaration is virtual
250 abstract bool isVirtual(const FuncDeclaration fd
) const;
253 * Marks the given function declaration as optional.
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
261 * functionDeclaration = the function declaration to be set as optional
262 * sc = the scope from the semantic phase
264 abstract void setAsOptional(FuncDeclaration functionDeclaration
, Scope
* sc
) const;
267 * Validates function declarations declared optional.
270 * functionDeclaration = the function declaration to validate
272 abstract void validateOptional(FuncDeclaration functionDeclaration
) const;
275 * Gets the parent of the given function declaration.
277 * Handles Objective-C static member functions, which are virtual functions
278 * of the metaclass, by returning the parent class declaration to the
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
286 * Returns: the parent
288 abstract ClassDeclaration
getParent(FuncDeclaration fd
,
289 ClassDeclaration cd
) const;
292 * Adds the given function to the list of Objective-C methods.
294 * This list will later be used output the necessary Objective-C module info.
297 * fd = the function declaration to be added to the list
298 * cd = the class declaration the function belongs to
300 abstract void addToClassMethodList(FuncDeclaration fd
,
301 ClassDeclaration cd
) const;
304 * Returns the `this` pointer of the given function declaration.
306 * This is only used for class/static methods. For instance methods, no
307 * Objective-C specialization is necessary.
310 * funcDeclaration = the function declaration to get the `this` pointer for
312 * Returns: the `this` pointer of the given function declaration, or `null`
313 * if the given function declaration is not an Objective-C method.
315 abstract inout(AggregateDeclaration
) isThis(inout FuncDeclaration funcDeclaration
) const;
318 * Creates the selector parameter for the given function declaration.
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.
325 * fd = the function declaration to create the parameter for
326 * sc = the scope from the semantic phase
328 * Returns: the newly created selector parameter or `null` for
329 * non-Objective-C functions
331 abstract VarDeclaration
createSelectorParameter(FuncDeclaration fd
, Scope
* sc
) const;
334 * Creates and sets the metaclass on the given class/interface declaration.
336 * Will only be performed on regular Objective-C classes, not on metaclasses.
339 * classDeclaration = the class/interface declaration to set the metaclass on
341 abstract void setMetaclass(InterfaceDeclaration interfaceDeclaration
, Scope
* sc
) const;
344 abstract void setMetaclass(ClassDeclaration classDeclaration
, Scope
* sc
) const;
347 * Returns Objective-C runtime metaclass of the given class declaration.
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.
356 * classDeclaration = The class declaration to return the metaclass of
358 * Returns: the Objective-C runtime metaclass of the given class declaration
360 abstract ClassDeclaration
getRuntimeMetaclass(ClassDeclaration classDeclaration
) const;
363 abstract void addSymbols(AttribDeclaration attribDeclaration
,
364 ClassDeclarations
* classes
, ClassDeclarations
* categories
) const;
367 abstract void addSymbols(ClassDeclaration classDeclaration
,
368 ClassDeclarations
* classes
, ClassDeclarations
* categories
) const;
371 * Issues a compile time error if the `.offsetof`/`.tupleof` property is
372 * used on a field of an Objective-C class.
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.
381 * To get the offset of a field that is correct at runtime, functionality
382 * from the Objective-C runtime can be used instead.
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
391 * $(LINK2 https://en.wikipedia.org/wiki/Fragile_binary_interface_problem,
392 * Fragile Binary Interface Problem)
395 * $(LINK2 https://developer.apple.com/documentation/objectivec/objective_c_runtime,
396 * Objective-C Runtime)
398 abstract void checkOffsetof(Expression expression
, AggregateDeclaration aggregateDeclaration
) const;
401 abstract void checkTupleof(Expression expression
, TypeClass type
) const;
404 extern(C
++) private final class Unsupported
: Objc
406 extern(D
) final this()
408 ObjcGlue
.initialize();
411 override void setObjc(ClassDeclaration cd
)
413 .error(cd
.loc
, "%s `%s` Objective-C classes not supported", cd
.kind
, cd
.toPrettyChars
);
416 override void setObjc(InterfaceDeclaration id
)
418 .error(id
.loc
, "%s `%s` Objective-C interfaces not supported", id
.kind
, id
.toPrettyChars
);
421 override const(char)* toPrettyChars(ClassDeclaration
, bool qualifyTypes
) const
423 assert(0, "Should never be called when Objective-C is not supported");
426 override void setSelector(FuncDeclaration
, Scope
*)
431 override void validateSelector(FuncDeclaration
)
436 override void checkLinkage(FuncDeclaration
)
441 override bool isVirtual(const FuncDeclaration
) const
443 assert(0, "Should never be called when Objective-C is not supported");
446 override void setAsOptional(FuncDeclaration
, Scope
*) const
451 override void validateOptional(FuncDeclaration
) const
456 override ClassDeclaration
getParent(FuncDeclaration
, ClassDeclaration cd
) const
461 override void addToClassMethodList(FuncDeclaration
, ClassDeclaration
) const
466 override inout(AggregateDeclaration
) isThis(inout FuncDeclaration funcDeclaration
) const
471 override VarDeclaration
createSelectorParameter(FuncDeclaration
, Scope
*) const
476 override void setMetaclass(InterfaceDeclaration
, Scope
*) const
481 override void setMetaclass(ClassDeclaration
, Scope
*) const
486 override ClassDeclaration
getRuntimeMetaclass(ClassDeclaration classDeclaration
) const
488 assert(0, "Should never be called when Objective-C is not supported");
491 override void addSymbols(AttribDeclaration attribDeclaration
,
492 ClassDeclarations
* classes
, ClassDeclarations
* categories
) const
497 override void addSymbols(ClassDeclaration classDeclaration
,
498 ClassDeclarations
* classes
, ClassDeclarations
* categories
) const
503 override void checkOffsetof(Expression expression
, AggregateDeclaration aggregateDeclaration
) const
508 override void checkTupleof(Expression expression
, TypeClass type
) const
514 extern(C
++) private final class Supported
: Objc
516 extern(D
) final this()
518 VersionCondition
.addPredefinedGlobalIdent("D_ObjectiveC");
520 ObjcGlue
.initialize();
521 ObjcSelector
._init();
524 override void setObjc(ClassDeclaration cd
)
526 cd
.classKind
= ClassKind
.objc
;
527 cd
.objc
.isExtern
= (cd
.storage_class
& STC
.extern_
) > 0;
530 override void setObjc(InterfaceDeclaration id
)
532 id
.classKind
= ClassKind
.objc
;
533 id
.objc
.isExtern
= true;
536 override const(char)* toPrettyChars(ClassDeclaration cd
, bool qualifyTypes
) const
538 return cd
.parent
.toPrettyChars(qualifyTypes
);
541 override void setSelector(FuncDeclaration fd
, Scope
* sc
)
543 foreachUda(fd
, sc
, (e
) {
544 if (!e
.isStructLiteralExp())
547 auto literal
= e
.isStructLiteralExp();
550 if (!isCoreUda(literal
.sd
, Id
.udaSelector
))
553 if (fd
.objc
.selector
)
555 .error(fd
.loc
, "%s `%s` can only have one Objective-C selector per method", fd
.kind
, fd
.toPrettyChars
);
559 assert(literal
.elements
.length
== 1);
560 auto se
= (*literal
.elements
)[0].toStringExp();
563 fd
.objc
.selector
= ObjcSelector
.lookup(se
.toUTF8(sc
).peekString().ptr
);
569 override void validateSelector(FuncDeclaration fd
)
571 if (!fd
.objc
.selector
)
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
);
580 override void checkLinkage(FuncDeclaration fd
)
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
);
586 override bool isVirtual(const FuncDeclaration fd
) const
594 if (fd
.toParent
.isInterfaceDeclaration
&& fd
.isFinal
)
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.
602 return !(kind
== Visibility
.Kind
.private_ || kind
== Visibility
.Kind
.package_
);
605 override void setAsOptional(FuncDeclaration fd
, Scope
* sc
) const
607 const count
= declaredAsOptionalCount(fd
, sc
);
608 fd
.objc
.isOptional
= count
> 0;
611 .error(fd
.loc
, "%s `%s` can only declare a function as optional once", fd
.kind
, fd
.toPrettyChars
);
614 /// Returns: the number of times `fd` has been declared as optional.
615 private int declaredAsOptionalCount(FuncDeclaration fd
, Scope
* sc
) const
619 foreachUda(fd
, sc
, (e
) {
623 auto typeExp
= e
.isTypeExp();
625 if (typeExp
.type
.ty
!= Tenum
)
628 auto typeEnum
= cast(TypeEnum
) typeExp
.type
;
630 if (isCoreUda(typeEnum
.sym
, Id
.udaOptional
))
639 override void validateOptional(FuncDeclaration fd
) const
641 if (!fd
.objc
.isOptional
)
644 if (fd
._linkage
!= LINK
.objc
)
646 .error(fd
.loc
, "%s `%s` only functions with Objective-C linkage can be declared as optional", fd
.kind
, fd
.toPrettyChars
);
648 const linkage
= linkageToString(fd
._linkage
);
650 errorSupplemental(fd
.loc
, "function is declared with %.*s linkage",
651 cast(uint) linkage
.length
, linkage
.ptr
);
654 auto parent
= fd
.parent
;
656 if (parent
&& parent
.isTemplateInstance())
658 .error(fd
.loc
, "%s `%s` template cannot be optional", fd
.kind
, fd
.toPrettyChars
);
659 parent
= parent
.parent
;
663 if (parent
&& !parent
.isInterfaceDeclaration())
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
);
670 override ClassDeclaration
getParent(FuncDeclaration fd
, ClassDeclaration cd
) const
677 if (cd
.classKind
== ClassKind
.objc
&& fd
.isStatic
&& !cd
.objc
.isMeta
)
678 return cd
.objc
.metaclass
;
683 override void addToClassMethodList(FuncDeclaration fd
, ClassDeclaration cd
) const
686 assert(fd
.parent
.isClassDeclaration
);
690 if (cd
.classKind
!= ClassKind
.objc
)
693 if (!fd
.objc
.selector
)
696 assert(fd
.isStatic ? cd
.objc
.isMeta
: !cd
.objc
.isMeta
);
698 cd
.objc
.methodList
~= fd
;
701 override inout(AggregateDeclaration
) isThis(inout FuncDeclaration funcDeclaration
) const
703 with(funcDeclaration
)
708 // Use Objective-C class object as 'this'
709 auto cd
= isMember2().isClassDeclaration();
711 if (cd
.classKind
== ClassKind
.objc
)
714 return cd
.objc
.metaclass
;
721 override VarDeclaration
createSelectorParameter(FuncDeclaration fd
, Scope
* sc
) const
724 assert(fd
.selectorParameter
is null);
728 if (!fd
.objc
.selector
)
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
);
742 override void setMetaclass(InterfaceDeclaration interfaceDeclaration
, Scope
* sc
) const
744 auto newMetaclass(Loc loc
, BaseClasses
* metaBases
)
746 auto ident
= createMetaclassIdentifier(interfaceDeclaration
);
747 return new InterfaceDeclaration(loc
, ident
, metaBases
);
750 .setMetaclass
!newMetaclass(interfaceDeclaration
, sc
);
753 override void setMetaclass(ClassDeclaration classDeclaration
, Scope
* sc
) const
755 auto newMetaclass(Loc loc
, BaseClasses
* metaBases
)
757 auto ident
= createMetaclassIdentifier(classDeclaration
);
758 return new ClassDeclaration(loc
, ident
, metaBases
, new Dsymbols(), 0);
761 .setMetaclass
!newMetaclass(classDeclaration
, sc
);
764 override ClassDeclaration
getRuntimeMetaclass(ClassDeclaration classDeclaration
) const
766 if (!classDeclaration
.objc
.metaclass
&& classDeclaration
.objc
.isMeta
)
768 if (classDeclaration
.baseClass
)
769 return getRuntimeMetaclass(classDeclaration
.baseClass
);
771 return classDeclaration
;
774 return classDeclaration
.objc
.metaclass
;
777 override void addSymbols(AttribDeclaration attribDeclaration
,
778 ClassDeclarations
* classes
, ClassDeclarations
* categories
) const
780 auto symbols
= attribDeclaration
.include(null);
785 foreach (symbol
; *symbols
)
786 symbol
.addObjcSymbols(classes
, categories
);
789 override void addSymbols(ClassDeclaration classDeclaration
,
790 ClassDeclarations
* classes
, ClassDeclarations
* categories
) const
792 with (classDeclaration
)
793 if (classKind
== ClassKind
.objc
&& !objc
.isExtern
&& !objc
.isMeta
)
794 classes
.push(classDeclaration
);
797 override void checkOffsetof(Expression expression
, AggregateDeclaration aggregateDeclaration
) const
799 if (aggregateDeclaration
.classKind
!= ClassKind
.objc
)
802 enum errorMessage
= "no property `offsetof` for member `%s` of type " ~
805 enum supplementalMessage
= "`offsetof` is not available for members " ~
806 "of Objective-C classes. Please use the Objective-C runtime instead";
808 error(expression
.loc
, errorMessage
, expression
.toChars(),
809 expression
.type
.toChars());
810 errorSupplemental(expression
.loc
, supplementalMessage
);
813 override void checkTupleof(Expression expression
, TypeClass type
) const
815 if (type
.sym
.classKind
!= ClassKind
.objc
)
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");
825 * Creates and sets the metaclass on the given class/interface declaration.
827 * Will only be performed on regular Objective-C classes, not on metaclasses.
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
834 private void setMetaclass(alias newMetaclass
, T
)(T classDeclaration
, Scope
* sc
)
835 if (is(T
== ClassDeclaration
) ||
is(T
== InterfaceDeclaration
))
837 static if (is(T
== ClassDeclaration
))
838 enum errorType
= "class";
840 enum errorType
= "interface";
842 with (classDeclaration
)
844 if (classKind
!= ClassKind
.objc || objc
.isMeta || objc
.metaclass
)
847 if (!objc
.identifier
)
848 objc
.identifier
= classDeclaration
.ident
;
850 auto metaBases
= new BaseClasses();
852 foreach (base
; baseclasses
.opSlice
)
854 auto baseCd
= base
.sym
;
857 if (baseCd
.classKind
== ClassKind
.objc
)
859 assert(baseCd
.objc
.metaclass
);
860 assert(baseCd
.objc
.metaclass
.objc
.isMeta
);
861 assert(baseCd
.objc
.metaclass
.type
.ty
== Tclass
);
863 auto metaBase
= new BaseClass(baseCd
.objc
.metaclass
.type
);
864 metaBase
.sym
= baseCd
.objc
.metaclass
;
865 metaBases
.push(metaBase
);
869 .error(classDeclaration
.loc
, "%s `%s` base " ~ errorType
~ " for an Objective-C " ~
870 errorType
~ " must be `extern (Objective-C)`", classDeclaration
.kind
, classDeclaration
.toPrettyChars
);
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
;
882 objc
.metaclass
.baseClass
= baseClass
.objc
.metaclass
;
884 members
.push(objc
.metaclass
);
885 objc
.metaclass
.addMember(sc
, classDeclaration
);
887 objc
.metaclass
.members
= new Dsymbols();
888 objc
.metaclass
.dsymbolSemantic(sc
);
892 private Identifier
createMetaclassIdentifier(ClassDeclaration classDeclaration
)
894 const name
= "class_" ~ classDeclaration
.ident
.toString
~ "_Meta";
895 return Identifier
.generateAnonymousId(name
);