| /** | |
| * Interfacing with Objective-C. | |
| * | |
| * Specification: $(LINK2 https://dlang.org/spec/objc_interface.html, Interfacing to Objective-C) | |
| * | |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved | |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) | |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) | |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/objc.d, _objc.d) | |
| * Documentation: https://dlang.org/phobos/dmd_objc.html | |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/objc.d | |
| */ | |
| module dmd.objc; | |
| import dmd.aggregate; | |
| import dmd.arraytypes; | |
| import dmd.astenums; | |
| import dmd.attrib; | |
| import dmd.cond; | |
| import dmd.dclass; | |
| import dmd.declaration; | |
| import dmd.denum; | |
| import dmd.dmangle; | |
| import dmd.dmodule; | |
| import dmd.dscope; | |
| import dmd.dstruct; | |
| import dmd.dsymbol; | |
| import dmd.dsymbolsem; | |
| import dmd.errors; | |
| import dmd.expression; | |
| import dmd.expressionsem; | |
| import dmd.func; | |
| import dmd.globals; | |
| import dmd.gluelayer; | |
| import dmd.hdrgen; | |
| import dmd.id; | |
| import dmd.identifier; | |
| import dmd.mtype; | |
| import dmd.root.array; | |
| import dmd.common.outbuffer; | |
| import dmd.root.stringtable; | |
| import dmd.target; | |
| import dmd.tokens; | |
| struct ObjcSelector | |
| { | |
| // MARK: Selector | |
| private __gshared StringTable!(ObjcSelector*) stringtable; | |
| private __gshared int incnum = 0; | |
| const(char)* stringvalue; | |
| size_t stringlen; | |
| size_t paramCount; | |
| extern (C++) static void _init() | |
| { | |
| stringtable._init(); | |
| } | |
| extern (D) this(const(char)* sv, size_t len, size_t pcount) | |
| { | |
| stringvalue = sv; | |
| stringlen = len; | |
| paramCount = pcount; | |
| } | |
| extern (D) static ObjcSelector* lookup(const(char)* s) | |
| { | |
| size_t len = 0; | |
| size_t pcount = 0; | |
| const(char)* i = s; | |
| while (*i != 0) | |
| { | |
| ++len; | |
| if (*i == ':') | |
| ++pcount; | |
| ++i; | |
| } | |
| return lookup(s, len, pcount); | |
| } | |
| extern (D) static ObjcSelector* lookup(const(char)* s, size_t len, size_t pcount) | |
| { | |
| auto sv = stringtable.update(s, len); | |
| ObjcSelector* sel = sv.value; | |
| if (!sel) | |
| { | |
| sel = new ObjcSelector(sv.toDchars(), len, pcount); | |
| sv.value = sel; | |
| } | |
| return sel; | |
| } | |
| extern (C++) static ObjcSelector* create(FuncDeclaration fdecl) | |
| { | |
| OutBuffer buf; | |
| TypeFunction ftype = cast(TypeFunction)fdecl.type; | |
| const id = fdecl.ident.toString(); | |
| const nparams = ftype.parameterList.length; | |
| // Special case: property setter | |
| if (ftype.isproperty && nparams == 1) | |
| { | |
| // rewrite "identifier" as "setIdentifier" | |
| char firstChar = id[0]; | |
| if (firstChar >= 'a' && firstChar <= 'z') | |
| firstChar = cast(char)(firstChar - 'a' + 'A'); | |
| buf.writestring("set"); | |
| buf.writeByte(firstChar); | |
| buf.write(id[1 .. id.length - 1]); | |
| buf.writeByte(':'); | |
| goto Lcomplete; | |
| } | |
| // write identifier in selector | |
| buf.write(id[]); | |
| // add mangled type and colon for each parameter | |
| if (nparams) | |
| { | |
| buf.writeByte('_'); | |
| foreach (i, fparam; ftype.parameterList) | |
| { | |
| mangleToBuffer(fparam.type, &buf); | |
| buf.writeByte(':'); | |
| } | |
| } | |
| Lcomplete: | |
| buf.writeByte('\0'); | |
| // the slice is not expected to include a terminating 0 | |
| return lookup(cast(const(char)*)buf[].ptr, buf.length - 1, nparams); | |
| } | |
| extern (D) const(char)[] toString() const pure | |
| { | |
| return stringvalue[0 .. stringlen]; | |
| } | |
| } | |
| private __gshared Objc _objc; | |
| Objc objc() | |
| { | |
| return _objc; | |
| } | |
| /** | |
| * Contains all data for a class declaration that is needed for the Objective-C | |
| * integration. | |
| */ | |
| extern (C++) struct ObjcClassDeclaration | |
| { | |
| /// `true` if this class is a metaclass. | |
| bool isMeta = false; | |
| /// `true` if this class is externally defined. | |
| bool isExtern = false; | |
| /// Name of this class. | |
| Identifier identifier; | |
| /// The class declaration this belongs to. | |
| ClassDeclaration classDeclaration; | |
| /// The metaclass of this class. | |
| ClassDeclaration metaclass; | |
| /// List of non-inherited methods. | |
| FuncDeclaration[] methodList; | |
| extern (D) this(ClassDeclaration classDeclaration) | |
| { | |
| this.classDeclaration = classDeclaration; | |
| } | |
| bool isRootClass() const | |
| { | |
| return classDeclaration.classKind == ClassKind.objc && | |
| !metaclass && | |
| !classDeclaration.baseClass; | |
| } | |
| } | |
| /** | |
| * Contains all data for a function declaration that is needed for the | |
| * Objective-C integration. | |
| */ | |
| extern (C++) struct ObjcFuncDeclaration | |
| { | |
| /// The method selector (member functions only). | |
| ObjcSelector* selector; | |
| /// The implicit selector parameter. | |
| VarDeclaration selectorParameter; | |
| /// `true` if this function declaration is declared optional. | |
| bool isOptional; | |
| } | |
| // Should be an interface | |
| extern(C++) abstract class Objc | |
| { | |
| static void _init() | |
| { | |
| if (target.objc.supported) | |
| _objc = new Supported; | |
| else | |
| _objc = new Unsupported; | |
| } | |
| /** | |
| * Deinitializes the global state of the compiler. | |
| * | |
| * This can be used to restore the state set by `_init` to its original | |
| * state. | |
| */ | |
| static void deinitialize() | |
| { | |
| _objc = _objc.init; | |
| } | |
| abstract void setObjc(ClassDeclaration cd); | |
| abstract void setObjc(InterfaceDeclaration); | |
| /** | |
| * Returns a pretty textual representation of the given class declaration. | |
| * | |
| * Params: | |
| * classDeclaration = the class declaration to return the textual representation for | |
| * qualifyTypes = `true` if types should be qualified in the result | |
| * | |
| * Returns: the textual representation | |
| */ | |
| abstract const(char)* toPrettyChars(ClassDeclaration classDeclaration, bool qualifyTypes) const; | |
| abstract void setSelector(FuncDeclaration, Scope* sc); | |
| abstract void validateSelector(FuncDeclaration fd); | |
| abstract void checkLinkage(FuncDeclaration fd); | |
| /** | |
| * Returns `true` if the given function declaration is virtual. | |
| * | |
| * Function declarations with Objective-C linkage and which are static or | |
| * final are considered virtual. | |
| * | |
| * Params: | |
| * fd = the function declaration to check if it's virtual | |
| * | |
| * Returns: `true` if the given function declaration is virtual | |
| */ | |
| abstract bool isVirtual(const FuncDeclaration fd) const; | |
| /** | |
| * Marks the given function declaration as optional. | |
| * | |
| * A function declaration is considered optional if it's annotated with the | |
| * UDA: `@(core.attribute.optional)`. Only function declarations inside | |
| * interface declarations and with Objective-C linkage can be declared as | |
| * optional. | |
| * | |
| * Params: | |
| * functionDeclaration = the function declaration to be set as optional | |
| * sc = the scope from the semantic phase | |
| */ | |
| abstract void setAsOptional(FuncDeclaration functionDeclaration, Scope* sc) const; | |
| /** | |
| * Validates function declarations declared optional. | |
| * | |
| * Params: | |
| * functionDeclaration = the function declaration to validate | |
| */ | |
| abstract void validateOptional(FuncDeclaration functionDeclaration) const; | |
| /** | |
| * Gets the parent of the given function declaration. | |
| * | |
| * Handles Objective-C static member functions, which are virtual functions | |
| * of the metaclass, by returning the parent class declaration to the | |
| * metaclass. | |
| * | |
| * Params: | |
| * fd = the function declaration to get the parent of | |
| * cd = the current parent, i.e. the class declaration the given function | |
| * declaration belongs to | |
| * | |
| * Returns: the parent | |
| */ | |
| abstract ClassDeclaration getParent(FuncDeclaration fd, | |
| ClassDeclaration cd) const; | |
| /** | |
| * Adds the given function to the list of Objective-C methods. | |
| * | |
| * This list will later be used output the necessary Objective-C module info. | |
| * | |
| * Params: | |
| * fd = the function declaration to be added to the list | |
| * cd = the class declaration the function belongs to | |
| */ | |
| abstract void addToClassMethodList(FuncDeclaration fd, | |
| ClassDeclaration cd) const; | |
| /** | |
| * Returns the `this` pointer of the given function declaration. | |
| * | |
| * This is only used for class/static methods. For instance methods, no | |
| * Objective-C specialization is necessary. | |
| * | |
| * Params: | |
| * funcDeclaration = the function declaration to get the `this` pointer for | |
| * | |
| * Returns: the `this` pointer of the given function declaration, or `null` | |
| * if the given function declaration is not an Objective-C method. | |
| */ | |
| abstract inout(AggregateDeclaration) isThis(inout FuncDeclaration funcDeclaration) const; | |
| /** | |
| * Creates the selector parameter for the given function declaration. | |
| * | |
| * Objective-C methods has an extra hidden parameter that comes after the | |
| * `this` parameter. The selector parameter is of the Objective-C type `SEL` | |
| * and contains the selector which this method was called with. | |
| * | |
| * Params: | |
| * fd = the function declaration to create the parameter for | |
| * sc = the scope from the semantic phase | |
| * | |
| * Returns: the newly created selector parameter or `null` for | |
| * non-Objective-C functions | |
| */ | |
| abstract VarDeclaration createSelectorParameter(FuncDeclaration fd, Scope* sc) const; | |
| /** | |
| * Creates and sets the metaclass on the given class/interface declaration. | |
| * | |
| * Will only be performed on regular Objective-C classes, not on metaclasses. | |
| * | |
| * Params: | |
| * classDeclaration = the class/interface declaration to set the metaclass on | |
| */ | |
| abstract void setMetaclass(InterfaceDeclaration interfaceDeclaration, Scope* sc) const; | |
| /// ditto | |
| abstract void setMetaclass(ClassDeclaration classDeclaration, Scope* sc) const; | |
| /** | |
| * Returns Objective-C runtime metaclass of the given class declaration. | |
| * | |
| * `ClassDeclaration.ObjcClassDeclaration.metaclass` contains the metaclass | |
| * from the semantic point of view. This function returns the metaclass from | |
| * the Objective-C runtime's point of view. Here, the metaclass of a | |
| * metaclass is the root metaclass, not `null`. The root metaclass's | |
| * metaclass is itself. | |
| * | |
| * Params: | |
| * classDeclaration = The class declaration to return the metaclass of | |
| * | |
| * Returns: the Objective-C runtime metaclass of the given class declaration | |
| */ | |
| abstract ClassDeclaration getRuntimeMetaclass(ClassDeclaration classDeclaration) const; | |
| /// | |
| abstract void addSymbols(AttribDeclaration attribDeclaration, | |
| ClassDeclarations* classes, ClassDeclarations* categories) const; | |
| /// | |
| abstract void addSymbols(ClassDeclaration classDeclaration, | |
| ClassDeclarations* classes, ClassDeclarations* categories) const; | |
| /** | |
| * Issues a compile time error if the `.offsetof`/`.tupleof` property is | |
| * used on a field of an Objective-C class. | |
| * | |
| * To solve the fragile base class problem in Objective-C, fields have a | |
| * dynamic offset instead of a static offset. The compiler outputs a | |
| * statically known offset which later the dynamic loader can update, if | |
| * necessary, when the application is loaded. Due to this behavior it | |
| * doesn't make sense to be able to get the offset of a field at compile | |
| * time, because this offset might not actually be the same at runtime. | |
| * | |
| * To get the offset of a field that is correct at runtime, functionality | |
| * from the Objective-C runtime can be used instead. | |
| * | |
| * Params: | |
| * expression = the `.offsetof`/`.tupleof` expression | |
| * aggregateDeclaration = the aggregate declaration the field of the | |
| * `.offsetof`/`.tupleof` expression belongs to | |
| * type = the type of the receiver of the `.tupleof` expression | |
| * | |
| * See_Also: | |
| * $(LINK2 https://en.wikipedia.org/wiki/Fragile_binary_interface_problem, | |
| * Fragile Binary Interface Problem) | |
| * | |
| * See_Also: | |
| * $(LINK2 https://developer.apple.com/documentation/objectivec/objective_c_runtime, | |
| * Objective-C Runtime) | |
| */ | |
| abstract void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const; | |
| /// ditto | |
| abstract void checkTupleof(Expression expression, TypeClass type) const; | |
| } | |
| extern(C++) private final class Unsupported : Objc | |
| { | |
| extern(D) final this() | |
| { | |
| ObjcGlue.initialize(); | |
| } | |
| override void setObjc(ClassDeclaration cd) | |
| { | |
| cd.error("Objective-C classes not supported"); | |
| } | |
| override void setObjc(InterfaceDeclaration id) | |
| { | |
| id.error("Objective-C interfaces not supported"); | |
| } | |
| override const(char)* toPrettyChars(ClassDeclaration, bool qualifyTypes) const | |
| { | |
| assert(0, "Should never be called when Objective-C is not supported"); | |
| } | |
| override void setSelector(FuncDeclaration, Scope*) | |
| { | |
| // noop | |
| } | |
| override void validateSelector(FuncDeclaration) | |
| { | |
| // noop | |
| } | |
| override void checkLinkage(FuncDeclaration) | |
| { | |
| // noop | |
| } | |
| override bool isVirtual(const FuncDeclaration) const | |
| { | |
| assert(0, "Should never be called when Objective-C is not supported"); | |
| } | |
| override void setAsOptional(FuncDeclaration, Scope*) const | |
| { | |
| // noop | |
| } | |
| override void validateOptional(FuncDeclaration) const | |
| { | |
| // noop | |
| } | |
| override ClassDeclaration getParent(FuncDeclaration, ClassDeclaration cd) const | |
| { | |
| return cd; | |
| } | |
| override void addToClassMethodList(FuncDeclaration, ClassDeclaration) const | |
| { | |
| // noop | |
| } | |
| override inout(AggregateDeclaration) isThis(inout FuncDeclaration funcDeclaration) const | |
| { | |
| return null; | |
| } | |
| override VarDeclaration createSelectorParameter(FuncDeclaration, Scope*) const | |
| { | |
| return null; | |
| } | |
| override void setMetaclass(InterfaceDeclaration, Scope*) const | |
| { | |
| // noop | |
| } | |
| override void setMetaclass(ClassDeclaration, Scope*) const | |
| { | |
| // noop | |
| } | |
| override ClassDeclaration getRuntimeMetaclass(ClassDeclaration classDeclaration) const | |
| { | |
| assert(0, "Should never be called when Objective-C is not supported"); | |
| } | |
| override void addSymbols(AttribDeclaration attribDeclaration, | |
| ClassDeclarations* classes, ClassDeclarations* categories) const | |
| { | |
| // noop | |
| } | |
| override void addSymbols(ClassDeclaration classDeclaration, | |
| ClassDeclarations* classes, ClassDeclarations* categories) const | |
| { | |
| // noop | |
| } | |
| override void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const | |
| { | |
| // noop | |
| } | |
| override void checkTupleof(Expression expression, TypeClass type) const | |
| { | |
| // noop | |
| } | |
| } | |
| extern(C++) private final class Supported : Objc | |
| { | |
| extern(D) final this() | |
| { | |
| VersionCondition.addPredefinedGlobalIdent("D_ObjectiveC"); | |
| ObjcGlue.initialize(); | |
| ObjcSelector._init(); | |
| } | |
| override void setObjc(ClassDeclaration cd) | |
| { | |
| cd.classKind = ClassKind.objc; | |
| cd.objc.isExtern = (cd.storage_class & STC.extern_) > 0; | |
| } | |
| override void setObjc(InterfaceDeclaration id) | |
| { | |
| id.classKind = ClassKind.objc; | |
| id.objc.isExtern = true; | |
| } | |
| override const(char)* toPrettyChars(ClassDeclaration cd, bool qualifyTypes) const | |
| { | |
| return cd.parent.toPrettyChars(qualifyTypes); | |
| } | |
| override void setSelector(FuncDeclaration fd, Scope* sc) | |
| { | |
| foreachUda(fd, sc, (e) { | |
| if (!e.isStructLiteralExp()) | |
| return 0; | |
| auto literal = e.isStructLiteralExp(); | |
| assert(literal.sd); | |
| if (!isCoreUda(literal.sd, Id.udaSelector)) | |
| return 0; | |
| if (fd.objc.selector) | |
| { | |
| fd.error("can only have one Objective-C selector per method"); | |
| return 1; | |
| } | |
| assert(literal.elements.dim == 1); | |
| auto se = (*literal.elements)[0].toStringExp(); | |
| assert(se); | |
| fd.objc.selector = ObjcSelector.lookup(se.toUTF8(sc).peekString().ptr); | |
| return 0; | |
| }); | |
| } | |
| override void validateSelector(FuncDeclaration fd) | |
| { | |
| if (!fd.objc.selector) | |
| return; | |
| TypeFunction tf = cast(TypeFunction)fd.type; | |
| if (fd.objc.selector.paramCount != tf.parameterList.parameters.dim) | |
| fd.error("number of colons in Objective-C selector must match number of parameters"); | |
| if (fd.parent && fd.parent.isTemplateInstance()) | |
| fd.error("template cannot have an Objective-C selector attached"); | |
| } | |
| override void checkLinkage(FuncDeclaration fd) | |
| { | |
| if (fd.linkage != LINK.objc && fd.objc.selector) | |
| fd.error("must have Objective-C linkage to attach a selector"); | |
| } | |
| override bool isVirtual(const FuncDeclaration fd) const | |
| in | |
| { | |
| assert(fd.selector); | |
| assert(fd.isMember); | |
| } | |
| do | |
| { | |
| if (fd.toParent.isInterfaceDeclaration && fd.isFinal) | |
| return false; | |
| // * final member functions are kept virtual with Objective-C linkage | |
| // because the Objective-C runtime always use dynamic dispatch. | |
| // * static member functions are kept virtual too, as they represent | |
| // methods of the metaclass. | |
| with (fd.visibility) | |
| return !(kind == Visibility.Kind.private_ || kind == Visibility.Kind.package_); | |
| } | |
| override void setAsOptional(FuncDeclaration fd, Scope* sc) const | |
| { | |
| const count = declaredAsOptionalCount(fd, sc); | |
| fd.objc.isOptional = count > 0; | |
| if (count > 1) | |
| fd.error("can only declare a function as optional once"); | |
| } | |
| /// Returns: the number of times `fd` has been declared as optional. | |
| private int declaredAsOptionalCount(FuncDeclaration fd , Scope* sc) const | |
| { | |
| int count; | |
| foreachUda(fd, sc, (e) { | |
| if (!e.isTypeExp()) | |
| return 0; | |
| auto typeExp = e.isTypeExp(); | |
| if (typeExp.type.ty != Tenum) | |
| return 0; | |
| auto typeEnum = cast(TypeEnum) typeExp.type; | |
| if (isCoreUda(typeEnum.sym, Id.udaOptional)) | |
| count++; | |
| return 0; | |
| }); | |
| return count; | |
| } | |
| override void validateOptional(FuncDeclaration fd) const | |
| { | |
| if (!fd.objc.isOptional) | |
| return; | |
| if (fd.linkage != LINK.objc) | |
| { | |
| fd.error("only functions with Objective-C linkage can be declared as optional"); | |
| const linkage = linkageToString(fd.linkage); | |
| errorSupplemental(fd.loc, "function is declared with %.*s linkage", | |
| cast(uint) linkage.length, linkage.ptr); | |
| } | |
| auto parent = fd.parent; | |
| if (parent && parent.isTemplateInstance()) | |
| { | |
| fd.error("template cannot be optional"); | |
| parent = parent.parent; | |
| assert(parent); | |
| } | |
| if (parent && !parent.isInterfaceDeclaration()) | |
| { | |
| fd.error("only functions declared inside interfaces can be optional"); | |
| errorSupplemental(fd.loc, "function is declared inside %s", fd.parent.kind); | |
| } | |
| } | |
| override ClassDeclaration getParent(FuncDeclaration fd, ClassDeclaration cd) const | |
| out(metaclass) | |
| { | |
| assert(metaclass); | |
| } | |
| do | |
| { | |
| if (cd.classKind == ClassKind.objc && fd.isStatic && !cd.objc.isMeta) | |
| return cd.objc.metaclass; | |
| else | |
| return cd; | |
| } | |
| override void addToClassMethodList(FuncDeclaration fd, ClassDeclaration cd) const | |
| in | |
| { | |
| assert(fd.parent.isClassDeclaration); | |
| } | |
| do | |
| { | |
| if (cd.classKind != ClassKind.objc) | |
| return; | |
| if (!fd.objc.selector) | |
| return; | |
| assert(fd.isStatic ? cd.objc.isMeta : !cd.objc.isMeta); | |
| cd.objc.methodList ~= fd; | |
| } | |
| override inout(AggregateDeclaration) isThis(inout FuncDeclaration funcDeclaration) const | |
| { | |
| with(funcDeclaration) | |
| { | |
| if (!objc.selector) | |
| return null; | |
| // Use Objective-C class object as 'this' | |
| auto cd = isMember2().isClassDeclaration(); | |
| if (cd.classKind == ClassKind.objc) | |
| { | |
| if (!cd.objc.isMeta) | |
| return cd.objc.metaclass; | |
| } | |
| return null; | |
| } | |
| } | |
| override VarDeclaration createSelectorParameter(FuncDeclaration fd, Scope* sc) const | |
| in | |
| { | |
| assert(fd.selectorParameter is null); | |
| } | |
| do | |
| { | |
| if (!fd.objc.selector) | |
| return null; | |
| auto ident = Identifier.generateAnonymousId("_cmd"); | |
| auto var = new VarDeclaration(fd.loc, Type.tvoidptr, ident, null); | |
| var.storage_class |= STC.parameter; | |
| var.dsymbolSemantic(sc); | |
| if (!sc.insert(var)) | |
| assert(false); | |
| var.parent = fd; | |
| return var; | |
| } | |
| override void setMetaclass(InterfaceDeclaration interfaceDeclaration, Scope* sc) const | |
| { | |
| auto newMetaclass(Loc loc, BaseClasses* metaBases) | |
| { | |
| auto ident = createMetaclassIdentifier(interfaceDeclaration); | |
| return new InterfaceDeclaration(loc, ident, metaBases); | |
| } | |
| .setMetaclass!newMetaclass(interfaceDeclaration, sc); | |
| } | |
| override void setMetaclass(ClassDeclaration classDeclaration, Scope* sc) const | |
| { | |
| auto newMetaclass(Loc loc, BaseClasses* metaBases) | |
| { | |
| auto ident = createMetaclassIdentifier(classDeclaration); | |
| return new ClassDeclaration(loc, ident, metaBases, new Dsymbols(), 0); | |
| } | |
| .setMetaclass!newMetaclass(classDeclaration, sc); | |
| } | |
| override ClassDeclaration getRuntimeMetaclass(ClassDeclaration classDeclaration) const | |
| { | |
| if (!classDeclaration.objc.metaclass && classDeclaration.objc.isMeta) | |
| { | |
| if (classDeclaration.baseClass) | |
| return getRuntimeMetaclass(classDeclaration.baseClass); | |
| else | |
| return classDeclaration; | |
| } | |
| else | |
| return classDeclaration.objc.metaclass; | |
| } | |
| override void addSymbols(AttribDeclaration attribDeclaration, | |
| ClassDeclarations* classes, ClassDeclarations* categories) const | |
| { | |
| auto symbols = attribDeclaration.include(null); | |
| if (!symbols) | |
| return; | |
| foreach (symbol; *symbols) | |
| symbol.addObjcSymbols(classes, categories); | |
| } | |
| override void addSymbols(ClassDeclaration classDeclaration, | |
| ClassDeclarations* classes, ClassDeclarations* categories) const | |
| { | |
| with (classDeclaration) | |
| if (classKind == ClassKind.objc && !objc.isExtern && !objc.isMeta) | |
| classes.push(classDeclaration); | |
| } | |
| override void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const | |
| { | |
| if (aggregateDeclaration.classKind != ClassKind.objc) | |
| return; | |
| enum errorMessage = "no property `offsetof` for member `%s` of type " ~ | |
| "`%s`"; | |
| enum supplementalMessage = "`offsetof` is not available for members " ~ | |
| "of Objective-C classes. Please use the Objective-C runtime instead"; | |
| expression.error(errorMessage, expression.toChars(), | |
| expression.type.toChars()); | |
| expression.errorSupplemental(supplementalMessage); | |
| } | |
| override void checkTupleof(Expression expression, TypeClass type) const | |
| { | |
| if (type.sym.classKind != ClassKind.objc) | |
| return; | |
| expression.error("no property `tupleof` for type `%s`", type.toChars()); | |
| expression.errorSupplemental("`tupleof` is not available for members " ~ | |
| "of Objective-C classes. Please use the Objective-C runtime instead"); | |
| } | |
| } | |
| /* | |
| * Creates and sets the metaclass on the given class/interface declaration. | |
| * | |
| * Will only be performed on regular Objective-C classes, not on metaclasses. | |
| * | |
| * Params: | |
| * newMetaclass = a function that returns the metaclass to set. This should | |
| * return the same type as `T`. | |
| * classDeclaration = the class/interface declaration to set the metaclass on | |
| */ | |
| private void setMetaclass(alias newMetaclass, T)(T classDeclaration, Scope* sc) | |
| if (is(T == ClassDeclaration) || is(T == InterfaceDeclaration)) | |
| { | |
| static if (is(T == ClassDeclaration)) | |
| enum errorType = "class"; | |
| else | |
| enum errorType = "interface"; | |
| with (classDeclaration) | |
| { | |
| if (classKind != ClassKind.objc || objc.isMeta || objc.metaclass) | |
| return; | |
| if (!objc.identifier) | |
| objc.identifier = classDeclaration.ident; | |
| auto metaBases = new BaseClasses(); | |
| foreach (base ; baseclasses.opSlice) | |
| { | |
| auto baseCd = base.sym; | |
| assert(baseCd); | |
| if (baseCd.classKind == ClassKind.objc) | |
| { | |
| assert(baseCd.objc.metaclass); | |
| assert(baseCd.objc.metaclass.objc.isMeta); | |
| assert(baseCd.objc.metaclass.type.ty == Tclass); | |
| auto metaBase = new BaseClass(baseCd.objc.metaclass.type); | |
| metaBase.sym = baseCd.objc.metaclass; | |
| metaBases.push(metaBase); | |
| } | |
| else | |
| { | |
| error("base " ~ errorType ~ " for an Objective-C " ~ | |
| errorType ~ " must be `extern (Objective-C)`"); | |
| } | |
| } | |
| objc.metaclass = newMetaclass(loc, metaBases); | |
| objc.metaclass.storage_class |= STC.static_; | |
| objc.metaclass.classKind = ClassKind.objc; | |
| objc.metaclass.objc.isMeta = true; | |
| objc.metaclass.objc.isExtern = objc.isExtern; | |
| objc.metaclass.objc.identifier = objc.identifier; | |
| if (baseClass) | |
| objc.metaclass.baseClass = baseClass.objc.metaclass; | |
| members.push(objc.metaclass); | |
| objc.metaclass.addMember(sc, classDeclaration); | |
| objc.metaclass.members = new Dsymbols(); | |
| objc.metaclass.dsymbolSemantic(sc); | |
| } | |
| } | |
| private Identifier createMetaclassIdentifier(ClassDeclaration classDeclaration) | |
| { | |
| const name = "class_" ~ classDeclaration.ident.toString ~ "_Meta"; | |
| return Identifier.generateAnonymousId(name); | |
| } |