/** * Compiler implementation of the * $(LINK2 http://www.dlang.org, D programming language). * * Copyright: Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Source: $(DMDSRC _objc.d) */ module ddmd.objc; import ddmd.arraytypes; import ddmd.cond; import ddmd.dclass; import ddmd.dmangle; import ddmd.dmodule; import ddmd.dscope; import ddmd.dstruct; import ddmd.expression; import ddmd.func; import ddmd.globals; import ddmd.gluelayer; import ddmd.id; import ddmd.identifier; import ddmd.mtype; import ddmd.root.outbuffer; import ddmd.root.stringtable; struct ObjcSelector { // MARK: Selector extern (C++) static __gshared StringTable stringtable; extern (C++) static __gshared StringTable vTableDispatchSelectors; extern (C++) static __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 (C++) 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 (C++) static ObjcSelector* lookup(const(char)* s, size_t len, size_t pcount) { StringValue* sv = stringtable.update(s, len); ObjcSelector* sel = cast(ObjcSelector*)sv.ptrvalue; if (!sel) { sel = new ObjcSelector(sv.toDchars(), len, pcount); sv.ptrvalue = cast(char*)sel; } return sel; } extern (C++) static ObjcSelector* create(FuncDeclaration fdecl) { OutBuffer buf; size_t pcount = 0; TypeFunction ftype = cast(TypeFunction)fdecl.type; const id = fdecl.ident.toString(); // Special case: property setter if (ftype.isproperty && ftype.parameters && ftype.parameters.dim == 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.ptr + 1, id.length - 1); buf.writeByte(':'); goto Lcomplete; } // write identifier in selector buf.write(id.ptr, id.length); // add mangled type and colon for each parameter if (ftype.parameters && ftype.parameters.dim) { buf.writeByte('_'); Parameters* arguments = ftype.parameters; size_t dim = Parameter.dim(arguments); for (size_t i = 0; i < dim; i++) { Parameter arg = Parameter.getNth(arguments, i); mangleToBuffer(arg.type, &buf); buf.writeByte(':'); } pcount = dim; } Lcomplete: buf.writeByte('\0'); return lookup(cast(const(char)*)buf.data, buf.size, pcount); } } struct Objc_ClassDeclaration { // true if this is an Objective-C class/interface bool objc; // MARK: Objc_ClassDeclaration extern (C++) bool isInterface() { return objc; } } struct Objc_FuncDeclaration { FuncDeclaration fdecl; // Objective-C method selector (member function only) ObjcSelector* selector; extern (D) this(FuncDeclaration fdecl) { this.fdecl = fdecl; } } // MARK: semantic extern (C++) void objc_ClassDeclaration_semantic_PASSinit_LINKobjc(ClassDeclaration cd) { if (global.params.hasObjectiveC) cd.objc.objc = true; else cd.error("Objective-C classes not supported"); } extern (C++) void objc_InterfaceDeclaration_semantic_objcExtern(InterfaceDeclaration id, Scope* sc) { if (sc.linkage == LINKobjc) { if (global.params.hasObjectiveC) id.objc.objc = true; else id.error("Objective-C interfaces not supported"); } } // MARK: semantic extern (C++) void objc_FuncDeclaration_semantic_setSelector(FuncDeclaration fd, Scope* sc) { import ddmd.tokens; if (!fd.userAttribDecl) return; Expressions* udas = fd.userAttribDecl.getAttributes(); arrayExpressionSemantic(udas, sc, true); for (size_t i = 0; i < udas.dim; i++) { Expression uda = (*udas)[i]; assert(uda); if (uda.op != TOKtuple) continue; Expressions* exps = (cast(TupleExp)uda).exps; for (size_t j = 0; j < exps.dim; j++) { Expression e = (*exps)[j]; assert(e); if (e.op != TOKstructliteral) continue; StructLiteralExp literal = cast(StructLiteralExp)e; assert(literal.sd); if (!objc_isUdaSelector(literal.sd)) continue; if (fd.objc.selector) { fd.error("can only have one Objective-C selector per method"); return; } assert(literal.elements.dim == 1); StringExp se = (*literal.elements)[0].toStringExp(); assert(se); fd.objc.selector = ObjcSelector.lookup(cast(const(char)*)se.toUTF8(sc).string); } } } extern (C++) bool objc_isUdaSelector(StructDeclaration sd) { if (sd.ident != Id.udaSelector || !sd.parent) return false; Module _module = sd.parent.isModule(); return _module && _module.isCoreModule(Id.attribute); } extern (C++) void objc_FuncDeclaration_semantic_validateSelector(FuncDeclaration fd) { if (!fd.objc.selector) return; TypeFunction tf = cast(TypeFunction)fd.type; if (fd.objc.selector.paramCount != tf.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"); } extern (C++) void objc_FuncDeclaration_semantic_checkLinkage(FuncDeclaration fd) { if (fd.linkage != LINKobjc && fd.objc.selector) fd.error("must have Objective-C linkage to attach a selector"); } // MARK: init extern (C++) void objc_tryMain_dObjc() { if (global.params.isOSX && global.params.is64bit) { global.params.hasObjectiveC = true; VersionCondition.addPredefinedGlobalIdent("D_ObjectiveC"); } } extern (C++) void objc_tryMain_init() { objc_initSymbols(); ObjcSelector._init(); }