| /** |
| * Interfacing with Objective-C. |
| * |
| * Specification: $(LINK2 https://dlang.org/spec/objc_interface.html, Interfacing to Objective-C) |
| * |
| * Copyright: Copyright (C) 1999-2023 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.location; |
| 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.length == 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.length) |
| 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); |
| } |