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