1 /**
2  * Glue code for Objective-C interop.
3  *
4  * Copyright:   Copyright (C) 2015-2020 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/objc_glue.d, _objc_glue.d)
8  * Documentation:  https://dlang.org/phobos/dmd_objc_glue.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/objc_glue.d
10  */
11 
12 module dmd.objc_glue;
13 
14 import core.stdc.stdio;
15 import core.stdc.stdlib;
16 import core.stdc.string;
17 
18 import dmd.aggregate;
19 import dmd.arraytypes;
20 import dmd.dclass;
21 import dmd.declaration;
22 import dmd.dmodule;
23 import dmd.dsymbol;
24 import dmd.expression;
25 import dmd.func;
26 import dmd.glue;
27 import dmd.identifier;
28 import dmd.mtype;
29 import dmd.objc;
30 import dmd.target;
31 
32 import dmd.root.stringtable;
33 
34 import dmd.backend.dt;
35 import dmd.backend.cc;
36 import dmd.backend.cdef;
37 import dmd.backend.el;
38 import dmd.backend.global;
39 import dmd.backend.oper;
40 import dmd.backend.outbuf;
41 import dmd.backend.ty;
42 import dmd.backend.type;
43 import dmd.backend.mach;
44 import dmd.backend.obj;
45 
46 private __gshared ObjcGlue _objc;
47 
48 ObjcGlue objc()
49 {
50     return _objc;
51 }
52 
53 // Should be an interface
54 extern(C++) abstract class ObjcGlue
55 {
56     static struct ElemResult
57     {
58         elem* ec;
59         elem* ethis;
60     }
61 
62     static void initialize()
63     {
64         if (target.objc.supported)
65             _objc = new Supported;
66         else
67             _objc = new Unsupported;
68     }
69 
70     abstract void setupMethodSelector(FuncDeclaration fd, elem** esel);
71 
72     abstract ElemResult setupMethodCall(FuncDeclaration fd, TypeFunction tf,
73         bool directcall, elem* ec, elem* ehidden, elem* ethis);
74 
75     abstract void setupEp(elem* esel, elem** ep, int leftToRight);
76     abstract void generateModuleInfo(Module module_);
77 
78     /// Returns: the given expression converted to an `elem` structure
79     abstract elem* toElem(ObjcClassReferenceExp e) const;
80 
81     /// Outputs the given Objective-C class to the object file.
82     abstract void toObjFile(ClassDeclaration classDeclaration) const;
83 
84     /**
85      * Adds the selector parameter to the given list of parameters.
86      *
87      * For Objective-C methods the selector parameter is added. For
88      * non-Objective-C methods `parameters` is unchanged.
89      *
90      * Params:
91      *  functionDeclaration = the function declaration to add the selector
92      *      parameter from
93      *  parameters = the list of parameters to add the selector parameter to
94      *  parameterCount = the number of parameters
95      *
96      * Returns: the new number of parameters
97      */
98     abstract size_t addSelectorParameterSymbol(
99         FuncDeclaration functionDeclaration,
100         Symbol** parameters, size_t parameterCount) const;
101 
102     /**
103      * Returns the offset of the given variable declaration `var`.
104      *
105      * This is used in a `DotVarExp` to get the offset of the variable the
106      * expression is accessing.
107      *
108      * Instance variables in Objective-C are non-fragile. That means that the
109      * base class can change (add or remove instance variables) without the
110      * subclasses needing to recompile or relink. This is implemented instance
111      * variables having a dynamic offset. This is achieved by going through an
112      * indirection in the form of a symbol generated in the binary. The compiler
113      * outputs the static offset in the generated symbol. Then, at load time,
114      * the symbol is updated with the correct offset, if necessary.
115      *
116      * Params:
117      *  var = the variable declaration to return the offset of
118      *  type = the type of the `DotVarExp`
119      *  offset = the existing offset
120      *
121      * Returns: a symbol containing the offset of the variable declaration
122      */
123     abstract elem* getOffset(VarDeclaration var, Type type, elem* offset) const;
124 }
125 
126 private:
127 
128 extern(C++) final class Unsupported : ObjcGlue
129 {
130     override void setupMethodSelector(FuncDeclaration fd, elem** esel)
131     {
132         // noop
133     }
134 
135     override ElemResult setupMethodCall(FuncDeclaration, TypeFunction, bool,
136         elem*, elem*, elem*)
137     {
138         assert(0, "Should never be called when Objective-C is not supported");
139     }
140 
141     override void setupEp(elem* esel, elem** ep, int reverse)
142     {
143         // noop
144     }
145 
146     override void generateModuleInfo(Module)
147     {
148         // noop
149     }
150 
151     override elem* toElem(ObjcClassReferenceExp e) const
152     {
153         assert(0, "Should never be called when Objective-C is not supported");
154     }
155 
156     override void toObjFile(ClassDeclaration classDeclaration) const
157     {
158         assert(0, "Should never be called when Objective-C is not supported");
159     }
160 
161     override size_t addSelectorParameterSymbol(FuncDeclaration, Symbol**,
162         size_t count) const
163     {
164         return count;
165     }
166 
167     override elem* getOffset(VarDeclaration var, Type type, elem* offset) const
168     {
169         return offset;
170     }
171 }
172 
173 extern(C++) final class Supported : ObjcGlue
174 {
175     extern (D) this()
176     {
177         Segments.initialize();
178         Symbols.initialize();
179     }
180 
181     override void setupMethodSelector(FuncDeclaration fd, elem** esel)
182     {
183         if (fd && fd.selector && !*esel)
184         {
185             *esel = el_var(Symbols.getMethVarRef(fd.selector.toString()));
186         }
187     }
188 
189     override ElemResult setupMethodCall(FuncDeclaration fd, TypeFunction tf,
190         bool directcall, elem* ec, elem* ehidden, elem* ethis)
191     {
192         import dmd.e2ir : addressElem;
193 
194         if (directcall) // super call
195         {
196             ElemResult result;
197             // call through Objective-C runtime dispatch
198             result.ec = el_var(Symbols.getMsgSendSuper(ehidden !is null));
199 
200             // need to change this pointer to a pointer to an two-word
201             // objc_super struct of the form { this ptr, class ptr }.
202             auto cd = fd.isThis.isClassDeclaration;
203             assert(cd, "call to objc_msgSendSuper with no class declaration");
204 
205             // faking objc_super type as delegate
206             auto classRef = el_var(Symbols.getClassReference(cd));
207             auto super_ = el_pair(TYdelegate, ethis, classRef);
208 
209             result.ethis = addressElem(super_, tf);
210 
211             return result;
212         }
213 
214         else
215         {
216             // make objc-style "virtual" call using dispatch function
217             assert(ethis);
218             Type tret = tf.next;
219 
220             ElemResult result = {
221                 ec: el_var(Symbols.getMsgSend(tret, ehidden !is null)),
222                 ethis: ethis
223             };
224 
225             return result;
226         }
227     }
228 
229     override void setupEp(elem* esel, elem** ep, int leftToRight)
230     {
231         if (esel)
232         {
233             // using objc-style "virtual" call
234             // add hidden argument (second to 'this') for selector used by dispatch function
235             if (leftToRight)
236                 *ep = el_param(esel, *ep);
237             else
238                 *ep = el_param(*ep, esel);
239         }
240     }
241 
242     override void generateModuleInfo(Module module_)
243     {
244         ClassDeclarations classes;
245         ClassDeclarations categories;
246 
247         module_.members.foreachDsymbol(m => m.addObjcSymbols(&classes, &categories));
248 
249         if (classes.length || categories.length || Symbols.hasSymbols)
250             Symbols.getModuleInfo(classes, categories);
251     }
252 
253     override elem* toElem(ObjcClassReferenceExp e) const
254     {
255         return el_var(Symbols.getClassReference(e.classDeclaration));
256     }
257 
258     override void toObjFile(ClassDeclaration classDeclaration) const
259     in
260     {
261         assert(classDeclaration !is null);
262         assert(classDeclaration.classKind == ClassKind.objc);
263     }
264     do
265     {
266         if (!classDeclaration.objc.isMeta)
267             ObjcClassDeclaration(classDeclaration, false).toObjFile();
268     }
269 
270     override size_t addSelectorParameterSymbol(FuncDeclaration fd,
271         Symbol** params, size_t count) const
272     in
273     {
274         assert(fd);
275     }
276     do
277     {
278         if (!fd.selector)
279             return count;
280 
281         assert(fd.selectorParameter);
282         auto selectorSymbol = fd.selectorParameter.toSymbol();
283         memmove(params + 1, params, count * params[0].sizeof);
284         params[0] = selectorSymbol;
285 
286         return count + 1;
287     }
288 
289     override elem* getOffset(VarDeclaration var, Type type, elem* offset) const
290     {
291         auto typeClass = type.isTypeClass;
292 
293         if (!typeClass || typeClass.sym.classKind != ClassKind.objc)
294             return offset;
295 
296         return el_var(ObjcClassDeclaration(typeClass.sym, false).getIVarOffset(var));
297     }
298 }
299 
300 struct Segments
301 {
302     enum Id
303     {
304         classlist,
305         classname,
306         classrefs,
307         const_,
308         data,
309         imageinfo,
310         ivar,
311         methname,
312         methtype,
313         selrefs
314     }
315 
316     private
317     {
318         __gshared int[Id] segments;
319         __gshared Segments[Id] segmentData;
320 
321         immutable(char*) sectionName;
322         immutable(char*) segmentName;
323         immutable int flags;
324         immutable int alignment;
325 
326         this(typeof(this.tupleof) tuple)
327         {
328             this.tupleof = tuple;
329         }
330 
331         static void initialize()
332         {
333             segmentData = [
334                 Id.classlist: Segments("__objc_classlist", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 3),
335                 Id.classname: Segments("__objc_classname", "__TEXT", S_CSTRING_LITERALS, 0),
336                 Id.classrefs: Segments("__objc_classrefs", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 3),
337                 Id.const_: Segments("__objc_const", "__DATA", S_REGULAR, 3),
338                 Id.data: Segments("__objc_data", "__DATA", S_REGULAR, 3),
339                 Id.imageinfo: Segments("__objc_imageinfo", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 0),
340                 Id.ivar: Segments("__objc_ivar", "__DATA", S_REGULAR, 3),
341                 Id.methname: Segments("__objc_methname", "__TEXT", S_CSTRING_LITERALS, 0),
342                 Id.methtype: Segments("__objc_methtype", "__TEXT", S_CSTRING_LITERALS, 0),
343                 Id.selrefs: Segments("__objc_selrefs", "__DATA", S_LITERAL_POINTERS | S_ATTR_NO_DEAD_STRIP, 3),
344             ];
345         }
346     }
347 
348     static int opIndex(Id id)
349     {
350         if (auto segment = id in segments)
351             return *segment;
352 
353         const seg = segmentData[id];
354 
355         version (OSX)
356         {
357             return segments[id] = Obj.getsegment(
358                 seg.sectionName,
359                 seg.segmentName,
360                 seg.alignment,
361                 seg.flags
362             );
363         }
364 
365         else
366         {
367             // This should never happen. If the platform is not OSX an error
368             // should have occurred sooner which should have prevented the
369             // code from getting here.
370             assert(0);
371         }
372     }
373 }
374 
375 struct Symbols
376 {
377 static:
378 
379     private __gshared
380     {
381         bool hasSymbols_ = false;
382 
383         Symbol* objc_msgSend = null;
384         Symbol* objc_msgSend_stret = null;
385         Symbol* objc_msgSend_fpret = null;
386         Symbol* objc_msgSend_fp2ret = null;
387 
388         Symbol* objc_msgSendSuper = null;
389         Symbol* objc_msgSendSuper_stret = null;
390 
391         Symbol* imageInfo = null;
392         Symbol* moduleInfo = null;
393 
394         Symbol* emptyCache = null;
395         Symbol* emptyVTable = null;
396 
397         // Cache for `_OBJC_METACLASS_$_`/`_OBJC_CLASS_$_` symbols.
398         StringTable!(Symbol*)* classNameTable = null;
399 
400         // Cache for `L_OBJC_CLASSLIST_REFERENCES_` symbols.
401         StringTable!(Symbol*)* classReferenceTable = null;
402 
403         StringTable!(Symbol*)* methVarNameTable = null;
404         StringTable!(Symbol*)* methVarRefTable = null;
405         StringTable!(Symbol*)* methVarTypeTable = null;
406 
407         // Cache for instance variable offsets
408         StringTable!(Symbol*)* ivarOffsetTable = null;
409     }
410 
411     void initialize()
412     {
413         initializeStringTables();
414     }
415 
416     private void initializeStringTables()
417     {
418         alias This = typeof(this);
419 
420         foreach (m ; __traits(allMembers, This))
421         {
422             static if (is(typeof(__traits(getMember, This, m)) == StringTable!(Symbol*)*))
423             {
424                 __traits(getMember, This, m) = new StringTable!(Symbol*)();
425                 __traits(getMember, This, m)._init();
426             }
427         }
428     }
429 
430     bool hasSymbols()
431     {
432         if (hasSymbols_)
433             return true;
434 
435         alias This = typeof(this);
436 
437         foreach (m ; __traits(allMembers, This))
438         {
439             static if (is(typeof(__traits(getMember, This, m)) == Symbol*))
440             {
441                 if (__traits(getMember, This, m) !is null)
442                     return true;
443             }
444         }
445 
446         return false;
447     }
448 
449     /**
450      * Convenience wrapper around `dmd.backend.global.symbol_name`.
451      *
452      * Allows to pass the name of the symbol as a D string.
453      */
454     Symbol* symbolName(const(char)[] name, int sclass, type* t)
455     {
456         return symbol_name(name.ptr, cast(uint) name.length, sclass, t);
457     }
458 
459     /**
460      * Gets a global symbol.
461      *
462      * Params:
463      *  name = the name of the symbol
464      *  t = the type of the symbol
465      *
466      * Returns: the symbol
467      */
468     Symbol* getGlobal(const(char)[] name, type* t = type_fake(TYnptr))
469     {
470         return symbolName(name, SCglobal, t);
471     }
472 
473     /**
474      * Gets a static symbol.
475      *
476      * Params:
477      *  name = the name of the symbol
478      *  t = the type of the symbol
479      *
480      * Returns: the symbol
481      */
482     Symbol* getStatic(const(char)[] name, type* t = type_fake(TYnptr))
483     {
484         return symbolName(name, SCstatic, t);
485     }
486 
487     Symbol* getCString(const(char)[] str, const(char)[] symbolName, Segments.Id segment)
488     {
489         hasSymbols_ = true;
490 
491         // create data
492         auto dtb = DtBuilder(0);
493         dtb.nbytes(cast(uint) (str.length + 1), str.toStringz());
494 
495         // find segment
496         auto seg = Segments[segment];
497 
498         // create symbol
499         auto s = getStatic(symbolName, type_allocn(TYarray, tstypes[TYchar]));
500         s.Sdt = dtb.finish();
501         s.Sseg = seg;
502         return s;
503     }
504 
505     Symbol* getMethVarName(const(char)[] name)
506     {
507         hasSymbols_ = true;
508 
509         auto stringValue = methVarNameTable.update(name);
510         auto symbol = stringValue.value;
511 
512         if (!symbol)
513         {
514             __gshared size_t classNameCount = 0;
515             char[42] buffer;
516             const symbolName = format(buffer, "L_OBJC_METH_VAR_NAME_%lu", classNameCount++);
517             symbol = getCString(name, symbolName, Segments.Id.methname);
518             stringValue.value = symbol;
519         }
520 
521         return symbol;
522     }
523 
524     Symbol* getMethVarName(Identifier ident)
525     {
526         return getMethVarName(ident.toString());
527     }
528 
529     Symbol* getMsgSend(Type returnType, bool hasHiddenArgument)
530     {
531         if (hasHiddenArgument)
532             return setMsgSendSymbol!("_objc_msgSend_stret")(TYhfunc);
533         // not sure if DMD can handle this
534         else if (returnType.ty == Tcomplex80)
535             return setMsgSendSymbol!("_objc_msgSend_fp2ret");
536         else if (returnType.ty == Tfloat80)
537             return setMsgSendSymbol!("_objc_msgSend_fpret");
538         else
539             return setMsgSendSymbol!("_objc_msgSend");
540 
541         assert(0);
542     }
543 
544     Symbol* getMsgSendSuper(bool hasHiddenArgument)
545     {
546         if (hasHiddenArgument)
547             return setMsgSendSymbol!("_objc_msgSendSuper_stret")(TYhfunc);
548         else
549             return setMsgSendSymbol!("_objc_msgSendSuper")(TYnfunc);
550     }
551 
552     Symbol* getImageInfo()
553     {
554         if (imageInfo)
555             return imageInfo;
556 
557         auto dtb = DtBuilder(0);
558         dtb.dword(0); // version
559         dtb.dword(64); // flags
560 
561         imageInfo = symbol_name("L_OBJC_IMAGE_INFO", SCstatic, type_allocn(TYarray, tstypes[TYchar]));
562         imageInfo.Sdt = dtb.finish();
563         imageInfo.Sseg = Segments[Segments.Id.imageinfo];
564         outdata(imageInfo);
565 
566         return imageInfo;
567     }
568 
569     Symbol* getModuleInfo(/*const*/ ref ClassDeclarations classes,
570         /*const*/ ref ClassDeclarations categories)
571     {
572         assert(!moduleInfo); // only allow once per object file
573 
574         auto dtb = DtBuilder(0);
575 
576         foreach (c; classes)
577             dtb.xoff(getClassName(c), 0);
578 
579         foreach (c; categories)
580             dtb.xoff(getClassName(c), 0);
581 
582         Symbol* symbol = symbol_name("L_OBJC_LABEL_CLASS_$", SCstatic, type_allocn(TYarray, tstypes[TYchar]));
583         symbol.Sdt = dtb.finish();
584         symbol.Sseg = Segments[Segments.Id.classlist];
585         outdata(symbol);
586 
587         getImageInfo(); // make sure we also generate image info
588 
589         return moduleInfo;
590     }
591 
592     /**
593      * Returns: the `_OBJC_METACLASS_$_`/`_OBJC_CLASS_$_` symbol for the given
594      *  class declaration.
595      */
596     Symbol* getClassName(ObjcClassDeclaration objcClass)
597     {
598         hasSymbols_ = true;
599 
600         const prefix = objcClass.isMeta ? "_OBJC_METACLASS_$_" : "_OBJC_CLASS_$_";
601         auto name = prefix ~ objcClass.classDeclaration.objc.identifier.toString();
602 
603         auto stringValue = classNameTable.update(name);
604         auto symbol = stringValue.value;
605 
606         if (symbol)
607             return symbol;
608 
609         symbol = getGlobal(name);
610         stringValue.value = symbol;
611 
612         return symbol;
613     }
614 
615     /// ditto
616     Symbol* getClassName(ClassDeclaration classDeclaration, bool isMeta = false)
617     in
618     {
619         assert(classDeclaration !is null);
620     }
621     do
622     {
623         return getClassName(ObjcClassDeclaration(classDeclaration, isMeta));
624     }
625 
626     /*
627      * Returns: the `L_OBJC_CLASSLIST_REFERENCES_$_` symbol for the given class
628      *  declaration.
629      */
630     Symbol* getClassReference(ClassDeclaration classDeclaration)
631     {
632         hasSymbols_ = true;
633 
634         auto name = classDeclaration.objc.identifier.toString();
635 
636         auto stringValue = classReferenceTable.update(name);
637         auto symbol = stringValue.value;
638 
639         if (symbol)
640             return symbol;
641 
642         auto dtb = DtBuilder(0);
643         auto className = getClassName(classDeclaration);
644         dtb.xoff(className, 0, TYnptr);
645 
646         auto segment = Segments[Segments.Id.classrefs];
647 
648         __gshared size_t classReferenceCount = 0;
649 
650         char[42] nameString;
651         auto result = format(nameString, "L_OBJC_CLASSLIST_REFERENCES_$_%lu", classReferenceCount++);
652         symbol = getStatic(result);
653         symbol.Sdt = dtb.finish();
654         symbol.Sseg = segment;
655         outdata(symbol);
656 
657         stringValue.value = symbol;
658 
659         return symbol;
660     }
661 
662     Symbol* getMethVarRef(const(char)[] name)
663     {
664         hasSymbols_ = true;
665 
666         auto stringValue = methVarRefTable.update(name);
667         auto refSymbol = stringValue.value;
668         if (refSymbol is null)
669         {
670             // create data
671             auto dtb = DtBuilder(0);
672             auto selector = getMethVarName(name);
673             dtb.xoff(selector, 0, TYnptr);
674 
675             // find segment
676             auto seg = Segments[Segments.Id.selrefs];
677 
678             // create symbol
679             __gshared size_t selectorCount = 0;
680             char[42] nameString;
681             sprintf(nameString.ptr, "L_OBJC_SELECTOR_REFERENCES_%lu", selectorCount);
682             refSymbol = symbol_name(nameString.ptr, SCstatic, type_fake(TYnptr));
683 
684             refSymbol.Sdt = dtb.finish();
685             refSymbol.Sseg = seg;
686             outdata(refSymbol);
687             stringValue.value = refSymbol;
688 
689             ++selectorCount;
690         }
691         return refSymbol;
692     }
693 
694     Symbol* getMethVarRef(const Identifier ident)
695     {
696         return getMethVarRef(ident.toString());
697     }
698 
699     /**
700      * Returns the Objective-C type encoding for the given type.
701      *
702      * The available type encodings are documented by Apple, available at
703      * $(LINK2 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100, Type Encoding).
704      * The type encodings can also be obtained by running an Objective-C
705      * compiler and using the `@encode()` compiler directive.
706      *
707      * Params:
708      *  type = the type to return the type encoding for
709      *
710      * Returns: a string containing the type encoding
711      */
712     string getTypeEncoding(Type type)
713     in
714     {
715         assert(type !is null);
716     }
717     do
718     {
719         enum assertMessage = "imaginary types are not supported by Objective-C";
720 
721         with (ENUMTY) switch (type.ty)
722         {
723             case Tvoid: return "v";
724             case Tbool: return "B";
725             case Tint8: return "c";
726             case Tuns8: return "C";
727             case Tchar: return "C";
728             case Tint16: return "s";
729             case Tuns16: return "S";
730             case Twchar: return "S";
731             case Tint32: return "i";
732             case Tuns32: return "I";
733             case Tdchar: return "I";
734             case Tint64: return "q";
735             case Tuns64: return "Q";
736             case Tfloat32: return "f";
737             case Tcomplex32: return "jf";
738             case Tfloat64: return "d";
739             case Tcomplex64: return "jd";
740             case Tfloat80: return "D";
741             case Tcomplex80: return "jD";
742             case Timaginary32: assert(false, assertMessage);
743             case Timaginary64: assert(false, assertMessage);
744             case Timaginary80: assert(false, assertMessage);
745             default: return "?"; // unknown
746             // TODO: add "*" char*, "#" Class, "@" id, ":" SEL
747             // TODO: add "^"<type> indirection and "^^" double indirection
748         }
749     }
750 
751     /**
752      * Returns: the `L_OBJC_METH_VAR_TYPE_` symbol containing the given
753      * type encoding.
754      */
755     Symbol* getMethVarType(const(char)[] typeEncoding)
756     {
757         hasSymbols_ = true;
758 
759         auto stringValue = methVarTypeTable.update(typeEncoding);
760         auto symbol = stringValue.value;
761 
762         if (symbol)
763             return symbol;
764 
765         __gshared size_t count = 0;
766         char[42] nameString;
767         const symbolName = format(nameString, "L_OBJC_METH_VAR_TYPE_%lu", count++);
768         symbol = getCString(typeEncoding, symbolName, Segments.Id.methtype);
769 
770         stringValue.value = symbol;
771         outdata(symbol);
772 
773         return symbol;
774     }
775 
776     /// ditto
777     Symbol* getMethVarType(Type[] types ...)
778     {
779         string typeCode;
780         typeCode.reserve(types.length);
781 
782         foreach (type; types)
783             typeCode ~= getTypeEncoding(type);
784 
785         return getMethVarType(typeCode);
786     }
787 
788     /// ditto
789     Symbol* getMethVarType(FuncDeclaration func)
790     {
791         Type[] types = [func.type.nextOf]; // return type first
792 
793         if (func.parameters)
794         {
795             types.reserve(func.parameters.length);
796 
797             foreach (e; *func.parameters)
798                 types ~= e.type;
799         }
800 
801         return getMethVarType(types);
802     }
803 
804     /// Returns: the externally defined `__objc_empty_cache` symbol
805     Symbol* getEmptyCache()
806     {
807         return emptyCache = emptyCache ? emptyCache : getGlobal("__objc_empty_cache");
808     }
809 
810     /// Returns: the externally defined `__objc_empty_vtable` symbol
811     Symbol* getEmptyVTable()
812     {
813         return emptyVTable = emptyVTable ? emptyVTable : getGlobal("__objc_empty_vtable");
814     }
815 
816     /// Returns: the `L_OBJC_CLASS_NAME_` symbol for a class with the given name
817     Symbol* getClassNameRo(const(char)[] name)
818     {
819         hasSymbols_ = true;
820 
821         auto stringValue = classNameTable.update(name);
822         auto symbol = stringValue.value;
823 
824         __gshared size_t count = 0;
825         char[42] nameString;
826         const symbolName = format(nameString, "L_OBJC_CLASS_NAME_%lu", count++);
827         symbol = getCString(name, symbolName, Segments.Id.classname);
828         stringValue.value = symbol;
829 
830         return symbol;
831     }
832 
833     /// ditto
834     Symbol* getClassNameRo(const Identifier ident)
835     {
836         return getClassNameRo(ident.toString());
837     }
838 
839     Symbol* getIVarOffset(ClassDeclaration cd, VarDeclaration var, bool outputSymbol)
840     {
841         hasSymbols_ = true;
842 
843         const className = cd.objc.identifier.toString;
844         const varName = var.ident.toString;
845         const name = "_OBJC_IVAR_$_" ~ className ~ '.' ~ varName;
846 
847         auto stringValue = ivarOffsetTable.update(name);
848         auto symbol = stringValue.value;
849 
850         if (!symbol)
851         {
852             symbol = getGlobal(name);
853             symbol.Sfl |= FLextern;
854             stringValue.value = symbol;
855         }
856 
857         if (!outputSymbol)
858             return symbol;
859 
860         auto dtb = DtBuilder(0);
861         dtb.size(var.offset);
862 
863         symbol.Sdt = dtb.finish();
864         symbol.Sseg = Segments[Segments.Id.ivar];
865         symbol.Sfl &= ~FLextern;
866 
867         outdata(symbol);
868 
869         return symbol;
870     }
871 
872     private Symbol* setMsgSendSymbol(string name)(tym_t ty = TYnfunc)
873     {
874         alias This = typeof(this);
875         enum fieldName = name[1 .. $];
876 
877         if (!__traits(getMember, This, fieldName))
878             __traits(getMember, This, fieldName) = getGlobal(name, type_fake(ty));
879 
880         return __traits(getMember, This, fieldName);
881     }
882 }
883 
884 private:
885 
886 /**
887  * Functionality for outputting symbols for a specific Objective-C class
888  * declaration.
889  */
890 struct ObjcClassDeclaration
891 {
892     /// Indicates what kind of class this is.
893     private enum Flags
894     {
895         /// Regular class.
896         regular = 0x00000,
897 
898         /// Meta class.
899         meta = 0x00001,
900 
901         /// Root class. A class without any base class.
902         root = 0x00002
903     }
904 
905     /// The class declaration
906     ClassDeclaration classDeclaration;
907 
908     /// `true` if this class is a metaclass.
909     bool isMeta;
910 
911     this(ClassDeclaration classDeclaration, bool isMeta)
912     in
913     {
914         assert(classDeclaration !is null);
915     }
916     do
917     {
918         this.classDeclaration = classDeclaration;
919         this.isMeta = isMeta;
920     }
921 
922     /**
923      * Outputs the class declaration to the object file.
924      *
925      * Returns: the exported symbol, that is, `_OBJC_METACLASS_$_` or
926      * `_OBJC_CLASS_$_`
927      */
928     Symbol* toObjFile()
929     {
930         if (classDeclaration.objc.isExtern)
931             return null; // only a declaration for an externally-defined class
932 
933         auto dtb = DtBuilder(0);
934         toDt(dtb);
935 
936         auto symbol = Symbols.getClassName(this);
937         symbol.Sdt = dtb.finish();
938         symbol.Sseg = Segments[Segments.Id.data];
939         outdata(symbol);
940 
941         return symbol;
942     }
943 
944 private:
945 
946     /**
947      * Outputs the class declaration to the object file.
948      *
949      * Params:
950      *  dtb = the `DtBuilder` to output the class declaration to
951      */
952     void toDt(ref DtBuilder dtb)
953     {
954         auto baseClassSymbol = classDeclaration.baseClass ?
955             Symbols.getClassName(classDeclaration.baseClass, isMeta) : null;
956 
957         dtb.xoff(getMetaclass(), 0); // pointer to metaclass
958         dtb.xoffOrNull(baseClassSymbol); // pointer to base class
959         dtb.xoff(Symbols.getEmptyCache(), 0);
960         dtb.xoff(Symbols.getEmptyVTable(), 0);
961         dtb.xoff(getClassRo(), 0);
962     }
963 
964     /// Returns: the name of the metaclass of this class declaration
965     Symbol* getMetaclass()
966     {
967         if (isMeta)
968         {
969             // metaclass: return root class's name
970             // (will be replaced with metaclass reference at load)
971 
972             auto metaclassDeclaration = classDeclaration;
973 
974             while (metaclassDeclaration.baseClass)
975                 metaclassDeclaration = metaclassDeclaration.baseClass;
976 
977             return Symbols.getClassName(metaclassDeclaration, true);
978         }
979 
980         else
981         {
982             // regular class: return metaclass with the same name
983             return ObjcClassDeclaration(classDeclaration, true).toObjFile();
984         }
985     }
986 
987     /**
988      * Returns: the `l_OBJC_CLASS_RO_$_`/`l_OBJC_METACLASS_RO_$_` symbol for
989      * this class declaration
990      */
991     Symbol* getClassRo()
992     {
993         auto dtb = DtBuilder(0);
994 
995         dtb.dword(flags);
996         dtb.dword(instanceStart);
997         dtb.dword(instanceSize);
998         dtb.dword(0); // reserved
999 
1000         dtb.size(0); // ivar layout
1001         dtb.xoff(Symbols.getClassNameRo(classDeclaration.ident), 0); // name of the class
1002 
1003         dtb.xoffOrNull(getMethodList()); // instance method list
1004         dtb.xoffOrNull(getProtocolList()); // protocol list
1005 
1006         if (isMeta)
1007         {
1008             dtb.size(0); // instance variable list
1009             dtb.size(0); // weak ivar layout
1010             dtb.size(0); // properties
1011         }
1012 
1013         else
1014         {
1015             dtb.xoffOrNull(getIVarList()); // instance variable list
1016             dtb.size(0); // weak ivar layout
1017             dtb.xoffOrNull(getPropertyList()); // properties
1018         }
1019 
1020         const prefix = isMeta ? "l_OBJC_METACLASS_RO_$_" : "l_OBJC_CLASS_RO_$_";
1021         const symbolName = prefix ~ classDeclaration.objc.identifier.toString();
1022         auto symbol = Symbols.getStatic(symbolName);
1023 
1024         symbol.Sdt = dtb.finish();
1025         symbol.Sseg = Segments[Segments.Id.const_];
1026         outdata(symbol);
1027 
1028         return symbol;
1029     }
1030 
1031     /**
1032      * Returns method list for this class declaration.
1033      *
1034      * This is a list of all methods defined in this class declaration, i.e.
1035      * methods with a body.
1036      *
1037      * Returns: the symbol for the method list, `l_OBJC_$_CLASS_METHODS_` or
1038      * `l_OBJC_$_INSTANCE_METHODS_`
1039      */
1040     Symbol* getMethodList()
1041     {
1042         /**
1043          * Returns the number of methods that should be added to the binary.
1044          *
1045          * Only methods with a body should be added.
1046          *
1047          * Params:
1048          *  members = the members of the class declaration
1049          */
1050         static int methodCount(Dsymbols* members)
1051         {
1052             int count;
1053 
1054             members.foreachDsymbol((member) {
1055                 const func = member.isFuncDeclaration;
1056 
1057                 if (func && func.fbody)
1058                     count++;
1059             });
1060 
1061             return count;
1062         }
1063 
1064         auto methods = isMeta ? classDeclaration.objc.metaclass.objc.methodList :
1065             classDeclaration.objc.methodList;
1066 
1067         const count = methodCount(methods);
1068 
1069         if (count == 0)
1070             return null;
1071 
1072         auto dtb = DtBuilder(0);
1073 
1074         dtb.dword(24); // _objc_method.sizeof
1075         dtb.dword(count); // method count
1076 
1077         methods.foreachDsymbol((method) {
1078             auto func = method.isFuncDeclaration;
1079 
1080             if (func && func.fbody)
1081             {
1082                 assert(func.selector);
1083                 dtb.xoff(func.selector.toNameSymbol(), 0); // method name
1084                 dtb.xoff(Symbols.getMethVarType(func), 0); // method type string
1085                 dtb.xoff(func.toSymbol(), 0); // function implementation
1086             }
1087         });
1088 
1089         const prefix = isMeta ? "l_OBJC_$_CLASS_METHODS_" : "l_OBJC_$_INSTANCE_METHODS_";
1090         const symbolName = prefix ~ classDeclaration.objc.identifier.toString();
1091         auto symbol = Symbols.getStatic(symbolName);
1092 
1093         symbol.Sdt = dtb.finish();
1094         symbol.Sseg = Segments[Segments.Id.const_];
1095 
1096         return symbol;
1097     }
1098 
1099     Symbol* getProtocolList()
1100     {
1101         // protocols are not supported yet
1102         return null;
1103     }
1104 
1105     Symbol* getIVarList()
1106     {
1107         if (isMeta || classDeclaration.fields.length == 0)
1108             return null;
1109 
1110         auto dtb = DtBuilder(0);
1111 
1112         dtb.dword(32); // entsize, _ivar_t.sizeof
1113         dtb.dword(cast(int) classDeclaration.fields.length); // ivar count
1114 
1115         foreach (field; classDeclaration.fields)
1116         {
1117             auto var = field.isVarDeclaration;
1118             assert(var);
1119             assert((var.storage_class & STC.static_) == 0);
1120 
1121             dtb.xoff(Symbols.getIVarOffset(classDeclaration, var, true), 0); // pointer to ivar offset
1122             dtb.xoff(Symbols.getMethVarName(var.ident), 0); // name
1123             dtb.xoff(Symbols.getMethVarType(var.type), 0); // type string
1124             dtb.dword(var.alignment);
1125             dtb.dword(cast(int) var.size(var.loc));
1126         }
1127 
1128         enum prefix = "l_OBJC_$_INSTANCE_VARIABLES_";
1129         const symbolName = prefix ~ classDeclaration.objc.identifier.toString();
1130         auto symbol = Symbols.getStatic(symbolName);
1131 
1132         symbol.Sdt = dtb.finish();
1133         symbol.Sseg = Segments[Segments.Id.const_];
1134 
1135         return symbol;
1136     }
1137 
1138     Symbol* getPropertyList()
1139     {
1140         // properties are not supported yet
1141         return null;
1142     }
1143 
1144     Symbol* getIVarOffset(VarDeclaration var)
1145     {
1146         if (var.toParent() is classDeclaration)
1147             return Symbols.getIVarOffset(classDeclaration, var, false);
1148 
1149         else if (classDeclaration.baseClass)
1150             return ObjcClassDeclaration(classDeclaration.baseClass, false)
1151                 .getIVarOffset(var);
1152 
1153         else
1154             assert(false, "Trying to get the base class of a root class");
1155     }
1156 
1157     /**
1158      * Returns the flags for this class declaration.
1159      *
1160      * That is, if this is a regular class, a metaclass and/or a root class.
1161      *
1162      * Returns: the flags
1163      */
1164     uint flags() const
1165     {
1166         uint flags = isMeta ? Flags.meta : Flags.regular;
1167 
1168         if (classDeclaration.objc.isRootClass)
1169             flags |= Flags.root;
1170 
1171         return flags;
1172     }
1173 
1174     /**
1175      * Returns the offset of where an instance of this class starts.
1176      *
1177      * For a metaclass this is always `40`. For a class with no instance
1178      * variables this is the size of the class declaration. For a class with
1179      * instance variables it's the offset of the first instance variable.
1180      *
1181      * Returns: the instance start
1182      */
1183     int instanceStart()
1184     {
1185         if (isMeta)
1186             return 40;
1187 
1188         const start = cast(uint) classDeclaration.size(classDeclaration.loc);
1189 
1190         if (!classDeclaration.members || classDeclaration.members.length == 0)
1191             return start;
1192 
1193         foreach (member; *classDeclaration.members)
1194         {
1195             auto var = member.isVarDeclaration;
1196 
1197             if (var && var.isField)
1198                 return var.offset;
1199         }
1200 
1201         return start;
1202     }
1203 
1204     /// Returns: the size of an instance of this class
1205     int instanceSize()
1206     {
1207         return isMeta ? 40 : cast(int) classDeclaration.size(classDeclaration.loc);
1208     }
1209 }
1210 
1211 /*
1212  * Formats the given arguments into the given buffer.
1213  *
1214  * Convenience wrapper around `snprintf`.
1215  *
1216  * Params:
1217  *  bufLength = length of the buffer
1218  *  buffer = the buffer where to store the result
1219  *  format = the format string
1220  *  args = the arguments to format
1221  *
1222  * Returns: the formatted result, a slice of the given buffer
1223  */
1224 char[] format(size_t bufLength, Args...)(return ref char[bufLength] buffer,
1225     const(char)* format, const Args args)
1226 {
1227     auto length = snprintf(buffer.ptr, buffer.length, format, args);
1228 
1229     assert(length >= 0, "An output error occurred");
1230     assert(length < buffer.length, "Output was truncated");
1231 
1232     return buffer[0 .. length];
1233 }
1234 
1235 /// Returns: the symbol of the given selector
1236 Symbol* toNameSymbol(const ObjcSelector* selector)
1237 {
1238     return Symbols.getMethVarName(selector.toString());
1239 }
1240 
1241 /**
1242  * Adds a reference to the given `symbol` or null if the symbol is null.
1243  *
1244  * Params:
1245  *  dtb = the dt builder to add the symbol to
1246  *  symbol = the symbol to add
1247  */
1248 void xoffOrNull(ref DtBuilder dtb, Symbol* symbol)
1249 {
1250     if (symbol)
1251         dtb.xoff(symbol, 0);
1252     else
1253         dtb.size(0);
1254 }
1255 
1256 /**
1257  * Converts the given D string to a null terminated C string.
1258  *
1259  * Asserts if `str` is longer than `maxLength`, with assertions enabled. With
1260  * assertions disabled it will truncate the result to `maxLength`.
1261  *
1262  * Params:
1263  *  maxLength = the max length of `str`
1264  *  str = the string to convert
1265  *  buf = the buffer where to allocate the result. By default this will be
1266  *      allocated in the caller scope using `alloca`. If the buffer is created
1267  *      by the callee it needs to be able to fit at least `str.length + 1` bytes
1268  *
1269  * Returns: the given string converted to a C string, a slice of `str` or the
1270  *  given buffer `buffer`
1271  */
1272 const(char)* toStringz(size_t maxLength = 4095)(in const(char)[] str,
1273     scope return void[] buffer = alloca(maxLength + 1)[0 .. maxLength + 1]) pure
1274 in
1275 {
1276     assert(maxLength >= str.length);
1277 }
1278 out(result)
1279 {
1280     assert(str.length == result.strlen);
1281 }
1282 do
1283 {
1284     if (str.length == 0)
1285         return "".ptr;
1286 
1287     const maxLength = buffer.length - 1;
1288     const len = str.length > maxLength ? maxLength : str.length;
1289     auto buf = cast(char[]) buffer[0 .. len + 1];
1290     buf[0 .. len] = str[0 .. len];
1291     buf[len] = '\0';
1292 
1293     return cast(const(char)*) buf.ptr;
1294 }