1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (c) 1999-2016 by Digital Mars, All Rights Reserved
6  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
7  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8  * Source:      $(DMDSRC _objc.d)
9  */
10 
11 module ddmd.objc;
12 
13 import ddmd.arraytypes;
14 import ddmd.cond;
15 import ddmd.dclass;
16 import ddmd.dmangle;
17 import ddmd.dmodule;
18 import ddmd.dscope;
19 import ddmd.dstruct;
20 import ddmd.expression;
21 import ddmd.func;
22 import ddmd.globals;
23 import ddmd.gluelayer;
24 import ddmd.id;
25 import ddmd.identifier;
26 import ddmd.mtype;
27 import ddmd.root.outbuffer;
28 import ddmd.root.stringtable;
29 
30 struct ObjcSelector
31 {
32     // MARK: Selector
33     extern (C++) static __gshared StringTable stringtable;
34     extern (C++) static __gshared StringTable vTableDispatchSelectors;
35     extern (C++) static __gshared int incnum = 0;
36     const(char)* stringvalue;
37     size_t stringlen;
38     size_t paramCount;
39 
40     extern (C++) static void _init()
41     {
42         stringtable._init();
43     }
44 
45     extern (D) this(const(char)* sv, size_t len, size_t pcount)
46     {
47         stringvalue = sv;
48         stringlen = len;
49         paramCount = pcount;
50     }
51 
52     extern (C++) static ObjcSelector* lookup(const(char)* s)
53     {
54         size_t len = 0;
55         size_t pcount = 0;
56         const(char)* i = s;
57         while (*i != 0)
58         {
59             ++len;
60             if (*i == ':')
61                 ++pcount;
62             ++i;
63         }
64         return lookup(s, len, pcount);
65     }
66 
67     extern (C++) static ObjcSelector* lookup(const(char)* s, size_t len, size_t pcount)
68     {
69         StringValue* sv = stringtable.update(s, len);
70         ObjcSelector* sel = cast(ObjcSelector*)sv.ptrvalue;
71         if (!sel)
72         {
73             sel = new ObjcSelector(sv.toDchars(), len, pcount);
74             sv.ptrvalue = cast(char*)sel;
75         }
76         return sel;
77     }
78 
79     extern (C++) static ObjcSelector* create(FuncDeclaration fdecl)
80     {
81         OutBuffer buf;
82         size_t pcount = 0;
83         TypeFunction ftype = cast(TypeFunction)fdecl.type;
84         const id = fdecl.ident.toString();
85         // Special case: property setter
86         if (ftype.isproperty && ftype.parameters && ftype.parameters.dim == 1)
87         {
88             // rewrite "identifier" as "setIdentifier"
89             char firstChar = id[0];
90             if (firstChar >= 'a' && firstChar <= 'z')
91                 firstChar = cast(char)(firstChar - 'a' + 'A');
92             buf.writestring("set");
93             buf.writeByte(firstChar);
94             buf.write(id.ptr + 1, id.length - 1);
95             buf.writeByte(':');
96             goto Lcomplete;
97         }
98         // write identifier in selector
99         buf.write(id.ptr, id.length);
100         // add mangled type and colon for each parameter
101         if (ftype.parameters && ftype.parameters.dim)
102         {
103             buf.writeByte('_');
104             Parameters* arguments = ftype.parameters;
105             size_t dim = Parameter.dim(arguments);
106             for (size_t i = 0; i < dim; i++)
107             {
108                 Parameter arg = Parameter.getNth(arguments, i);
109                 mangleToBuffer(arg.type, &buf);
110                 buf.writeByte(':');
111             }
112             pcount = dim;
113         }
114     Lcomplete:
115         buf.writeByte('\0');
116         return lookup(cast(const(char)*)buf.data, buf.size, pcount);
117     }
118 }
119 
120 struct Objc_ClassDeclaration
121 {
122     // true if this is an Objective-C class/interface
123     bool objc;
124 
125     // MARK: Objc_ClassDeclaration
126     extern (C++) bool isInterface()
127     {
128         return objc;
129     }
130 }
131 
132 struct Objc_FuncDeclaration
133 {
134     FuncDeclaration fdecl;
135     // Objective-C method selector (member function only)
136     ObjcSelector* selector;
137 
138     extern (D) this(FuncDeclaration fdecl)
139     {
140         this.fdecl = fdecl;
141     }
142 }
143 
144 // MARK: semantic
145 extern (C++) void objc_ClassDeclaration_semantic_PASSinit_LINKobjc(ClassDeclaration cd)
146 {
147     if (global.params.hasObjectiveC)
148         cd.objc.objc = true;
149     else
150         cd.error("Objective-C classes not supported");
151 }
152 
153 extern (C++) void objc_InterfaceDeclaration_semantic_objcExtern(InterfaceDeclaration id, Scope* sc)
154 {
155     if (sc.linkage == LINKobjc)
156     {
157         if (global.params.hasObjectiveC)
158             id.objc.objc = true;
159         else
160             id.error("Objective-C interfaces not supported");
161     }
162 }
163 
164 // MARK: semantic
165 extern (C++) void objc_FuncDeclaration_semantic_setSelector(FuncDeclaration fd, Scope* sc)
166 {
167     import ddmd.tokens;
168 
169     if (!fd.userAttribDecl)
170         return;
171     Expressions* udas = fd.userAttribDecl.getAttributes();
172     arrayExpressionSemantic(udas, sc, true);
173     for (size_t i = 0; i < udas.dim; i++)
174     {
175         Expression uda = (*udas)[i];
176         assert(uda);
177         if (uda.op != TOKtuple)
178             continue;
179         Expressions* exps = (cast(TupleExp)uda).exps;
180         for (size_t j = 0; j < exps.dim; j++)
181         {
182             Expression e = (*exps)[j];
183             assert(e);
184             if (e.op != TOKstructliteral)
185                 continue;
186             StructLiteralExp literal = cast(StructLiteralExp)e;
187             assert(literal.sd);
188             if (!objc_isUdaSelector(literal.sd))
189                 continue;
190             if (fd.objc.selector)
191             {
192                 fd.error("can only have one Objective-C selector per method");
193                 return;
194             }
195             assert(literal.elements.dim == 1);
196             StringExp se = (*literal.elements)[0].toStringExp();
197             assert(se);
198             fd.objc.selector = ObjcSelector.lookup(cast(const(char)*)se.toUTF8(sc).string);
199         }
200     }
201 }
202 
203 extern (C++) bool objc_isUdaSelector(StructDeclaration sd)
204 {
205     if (sd.ident != Id.udaSelector || !sd.parent)
206         return false;
207     Module _module = sd.parent.isModule();
208     return _module && _module.isCoreModule(Id.attribute);
209 }
210 
211 extern (C++) void objc_FuncDeclaration_semantic_validateSelector(FuncDeclaration fd)
212 {
213     if (!fd.objc.selector)
214         return;
215     TypeFunction tf = cast(TypeFunction)fd.type;
216     if (fd.objc.selector.paramCount != tf.parameters.dim)
217         fd.error("number of colons in Objective-C selector must match number of parameters");
218     if (fd.parent && fd.parent.isTemplateInstance())
219         fd.error("template cannot have an Objective-C selector attached");
220 }
221 
222 extern (C++) void objc_FuncDeclaration_semantic_checkLinkage(FuncDeclaration fd)
223 {
224     if (fd.linkage != LINKobjc && fd.objc.selector)
225         fd.error("must have Objective-C linkage to attach a selector");
226 }
227 
228 // MARK: init
229 extern (C++) void objc_tryMain_dObjc()
230 {
231     if (global.params.isOSX && global.params.is64bit)
232     {
233         global.params.hasObjectiveC = true;
234         VersionCondition.addPredefinedGlobalIdent("D_ObjectiveC");
235     }
236 }
237 
238 extern (C++) void objc_tryMain_init()
239 {
240     objc_initSymbols();
241     ObjcSelector._init();
242 }