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 import dmd.root.array;
34 
35 import dmd.backend.dt;
36 import dmd.backend.cc;
37 import dmd.backend.cdef;
38 import dmd.backend.el;
39 import dmd.backend.global;
40 import dmd.backend.oper;
41 import dmd.backend.outbuf;
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         data,
324         imageinfo,
325         ivar,
326         methname,
327         methtype,
328         selrefs
329     }
330 
331     private
332     {
333         __gshared int[Id] segments;
334         __gshared Segments[Id] segmentData;
335 
336         immutable(char*) sectionName;
337         immutable(char*) segmentName;
338         immutable int flags;
339         immutable int alignment;
340 
341         this(typeof(this.tupleof) tuple)
342         {
343             this.tupleof = tuple;
344         }
345 
346         static void initialize()
347         {
348             segmentData = [
349                 Id.classlist: Segments("__objc_classlist", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 3),
350                 Id.classname: Segments("__objc_classname", "__TEXT", S_CSTRING_LITERALS, 0),
351                 Id.classrefs: Segments("__objc_classrefs", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 3),
352                 Id.const_: Segments("__objc_const", "__DATA", S_REGULAR, 3),
353                 Id.data: Segments("__objc_data", "__DATA", S_REGULAR, 3),
354                 Id.imageinfo: Segments("__objc_imageinfo", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 0),
355                 Id.ivar: Segments("__objc_ivar", "__DATA", S_REGULAR, 3),
356                 Id.methname: Segments("__objc_methname", "__TEXT", S_CSTRING_LITERALS, 0),
357                 Id.methtype: Segments("__objc_methtype", "__TEXT", S_CSTRING_LITERALS, 0),
358                 Id.selrefs: Segments("__objc_selrefs", "__DATA", S_LITERAL_POINTERS | S_ATTR_NO_DEAD_STRIP, 3),
359             ];
360         }
361     }
362 
363     /// Resets the segments.
364     static void reset()
365     {
366         clearCache();
367     }
368 
369     // Clears any caches.
370     private static void clearCache()
371     {
372         segments.clear;
373     }
374 
375     static int opIndex(Id id)
376     {
377         if (auto segment = id in segments)
378             return *segment;
379 
380         const seg = segmentData[id];
381 
382         version (OSX)
383         {
384             return segments[id] = Obj.getsegment(
385                 seg.sectionName,
386                 seg.segmentName,
387                 seg.alignment,
388                 seg.flags
389             );
390         }
391 
392         else
393         {
394             // This should never happen. If the platform is not OSX an error
395             // should have occurred sooner which should have prevented the
396             // code from getting here.
397             assert(0);
398         }
399     }
400 }
401 
402 struct Symbols
403 {
404 static:
405 
406     private __gshared
407     {
408         alias SymbolCache = StringTable!(Symbol*)*;
409 
410         bool hasSymbols_ = false;
411 
412         Symbol* objc_msgSend = null;
413         Symbol* objc_msgSend_stret = null;
414         Symbol* objc_msgSend_fpret = null;
415         Symbol* objc_msgSend_fp2ret = null;
416 
417         Symbol* objc_msgSendSuper = null;
418         Symbol* objc_msgSendSuper_stret = null;
419 
420         Symbol* imageInfo = null;
421         Symbol* moduleInfo = null;
422 
423         Symbol* emptyCache = null;
424         Symbol* emptyVTable = null;
425 
426         // Cache for `_OBJC_METACLASS_$_`/`_OBJC_CLASS_$_` symbols.
427         SymbolCache classNameTable = null;
428 
429         // Cache for `L_OBJC_CLASSLIST_REFERENCES_` symbols.
430         SymbolCache classReferenceTable = null;
431 
432         SymbolCache methVarNameTable = null;
433         SymbolCache methVarRefTable = null;
434         SymbolCache methVarTypeTable = null;
435 
436         // Cache for instance variable offsets
437         SymbolCache ivarOffsetTable = null;
438     }
439 
440     void initialize()
441     {
442         initializeStringTables();
443     }
444 
445     private void initializeStringTables()
446     {
447         alias This = typeof(this);
448 
449         foreach (m ; __traits(allMembers, This))
450         {
451             static if (is(typeof(__traits(getMember, This, m)) == SymbolCache))
452             {
453                 __traits(getMember, This, m) = new StringTable!(Symbol*)();
454                 __traits(getMember, This, m)._init();
455             }
456         }
457     }
458 
459     /// Resets the symbols.
460     void reset()
461     {
462         clearCache();
463         resetSymbolCache();
464     }
465 
466     // Clears any caches.
467     private void clearCache()
468     {
469         alias This = typeof(this);
470 
471         foreach (m ; __traits(allMembers, This))
472         {
473             static if (is(typeof(__traits(getMember, This, m)) == Symbol*))
474                 __traits(getMember, This, m) = null;
475         }
476     }
477 
478     // Resets the symbol caches.
479     private void resetSymbolCache()
480     {
481         alias This = typeof(this);
482 
483         foreach (m ; __traits(allMembers, This))
484         {
485             static if (is(typeof(__traits(getMember, This, m)) == SymbolCache))
486                 __traits(getMember, This, m).reset();
487         }
488     }
489 
490     bool hasSymbols()
491     {
492         if (hasSymbols_)
493             return true;
494 
495         alias This = typeof(this);
496 
497         foreach (m ; __traits(allMembers, This))
498         {
499             static if (is(typeof(__traits(getMember, This, m)) == Symbol*))
500             {
501                 if (__traits(getMember, This, m) !is null)
502                     return true;
503             }
504         }
505 
506         return false;
507     }
508 
509     /**
510      * Convenience wrapper around `dmd.backend.global.symbol_name`.
511      *
512      * Allows to pass the name of the symbol as a D string.
513      */
514     Symbol* symbolName(const(char)[] name, int sclass, type* t)
515     {
516         return symbol_name(name.ptr, cast(uint) name.length, sclass, t);
517     }
518 
519     /**
520      * Gets a global symbol.
521      *
522      * Params:
523      *  name = the name of the symbol
524      *  t = the type of the symbol
525      *
526      * Returns: the symbol
527      */
528     Symbol* getGlobal(const(char)[] name, type* t = type_fake(TYnptr))
529     {
530         return symbolName(name, SCglobal, t);
531     }
532 
533     /**
534      * Gets a static symbol.
535      *
536      * Params:
537      *  name = the name of the symbol
538      *  t = the type of the symbol
539      *
540      * Returns: the symbol
541      */
542     Symbol* getStatic(const(char)[] name, type* t = type_fake(TYnptr))
543     {
544         return symbolName(name, SCstatic, t);
545     }
546 
547     Symbol* getCString(const(char)[] str, const(char)[] symbolName, Segments.Id segment)
548     {
549         hasSymbols_ = true;
550 
551         // create data
552         auto dtb = DtBuilder(0);
553         dtb.nbytes(cast(uint) (str.length + 1), str.toStringz());
554 
555         // find segment
556         auto seg = Segments[segment];
557 
558         // create symbol
559         auto s = getStatic(symbolName, type_allocn(TYarray, tstypes[TYchar]));
560         s.Sdt = dtb.finish();
561         s.Sseg = seg;
562         return s;
563     }
564 
565     Symbol* getMethVarName(const(char)[] name)
566     {
567         return cache(name, methVarNameTable, {
568             __gshared size_t classNameCount = 0;
569             char[42] buffer;
570             const symbolName = format(buffer, "L_OBJC_METH_VAR_NAME_%lu", classNameCount++);
571 
572             return getCString(name, symbolName, Segments.Id.methname);
573         });
574     }
575 
576     Symbol* getMethVarName(Identifier ident)
577     {
578         return getMethVarName(ident.toString());
579     }
580 
581     Symbol* getMsgSend(Type returnType, bool hasHiddenArgument)
582     {
583         if (hasHiddenArgument)
584             return setMsgSendSymbol!("_objc_msgSend_stret")(TYhfunc);
585         // not sure if DMD can handle this
586         else if (returnType.ty == Tcomplex80)
587             return setMsgSendSymbol!("_objc_msgSend_fp2ret");
588         else if (returnType.ty == Tfloat80)
589             return setMsgSendSymbol!("_objc_msgSend_fpret");
590         else
591             return setMsgSendSymbol!("_objc_msgSend");
592 
593         assert(0);
594     }
595 
596     Symbol* getMsgSendSuper(bool hasHiddenArgument)
597     {
598         if (hasHiddenArgument)
599             return setMsgSendSymbol!("_objc_msgSendSuper_stret")(TYhfunc);
600         else
601             return setMsgSendSymbol!("_objc_msgSendSuper")(TYnfunc);
602     }
603 
604     Symbol* getImageInfo()
605     {
606         if (imageInfo)
607             return imageInfo;
608 
609         auto dtb = DtBuilder(0);
610         dtb.dword(0); // version
611         dtb.dword(64); // flags
612 
613         imageInfo = symbol_name("L_OBJC_IMAGE_INFO", SCstatic, type_allocn(TYarray, tstypes[TYchar]));
614         imageInfo.Sdt = dtb.finish();
615         imageInfo.Sseg = Segments[Segments.Id.imageinfo];
616         outdata(imageInfo);
617 
618         return imageInfo;
619     }
620 
621     Symbol* getModuleInfo(/*const*/ ref ClassDeclarations classes,
622         /*const*/ ref ClassDeclarations categories)
623     {
624         assert(!moduleInfo); // only allow once per object file
625 
626         auto dtb = DtBuilder(0);
627 
628         foreach (c; classes)
629             dtb.xoff(getClassName(c), 0);
630 
631         foreach (c; categories)
632             dtb.xoff(getClassName(c), 0);
633 
634         Symbol* symbol = symbol_name("L_OBJC_LABEL_CLASS_$", SCstatic, type_allocn(TYarray, tstypes[TYchar]));
635         symbol.Sdt = dtb.finish();
636         symbol.Sseg = Segments[Segments.Id.classlist];
637         outdata(symbol);
638 
639         getImageInfo(); // make sure we also generate image info
640 
641         return moduleInfo;
642     }
643 
644     /**
645      * Returns: the `_OBJC_METACLASS_$_`/`_OBJC_CLASS_$_` symbol for the given
646      *  class declaration.
647      */
648     Symbol* getClassName(ObjcClassDeclaration objcClass)
649     {
650         hasSymbols_ = true;
651 
652         const prefix = objcClass.isMeta ? "_OBJC_METACLASS_$_" : "_OBJC_CLASS_$_";
653         auto name = prefix ~ objcClass.classDeclaration.objc.identifier.toString();
654 
655         return cache(name, classNameTable, () => getGlobal(name));
656     }
657 
658     /// ditto
659     Symbol* getClassName(ClassDeclaration classDeclaration, bool isMeta = false)
660     in
661     {
662         assert(classDeclaration !is null);
663     }
664     do
665     {
666         return getClassName(ObjcClassDeclaration(classDeclaration, isMeta));
667     }
668 
669     /*
670      * Returns: the `L_OBJC_CLASSLIST_REFERENCES_$_` symbol for the given class
671      *  declaration.
672      */
673     Symbol* getClassReference(ClassDeclaration classDeclaration)
674     {
675         hasSymbols_ = true;
676 
677         auto name = classDeclaration.objc.identifier.toString();
678 
679         return cache(name, classReferenceTable, {
680             auto dtb = DtBuilder(0);
681             auto className = getClassName(classDeclaration);
682             dtb.xoff(className, 0, TYnptr);
683 
684             auto segment = Segments[Segments.Id.classrefs];
685 
686             __gshared size_t classReferenceCount = 0;
687 
688             char[42] nameString;
689             auto result = format(nameString, "L_OBJC_CLASSLIST_REFERENCES_$_%lu", classReferenceCount++);
690             auto symbol = getStatic(result);
691             symbol.Sdt = dtb.finish();
692             symbol.Sseg = segment;
693             outdata(symbol);
694 
695             return symbol;
696         });
697     }
698 
699     Symbol* getMethVarRef(const(char)[] name)
700     {
701         return cache(name, methVarRefTable, {
702             // create data
703             auto dtb = DtBuilder(0);
704             auto selector = getMethVarName(name);
705             dtb.xoff(selector, 0, TYnptr);
706 
707             // find segment
708             auto seg = Segments[Segments.Id.selrefs];
709 
710             // create symbol
711             __gshared size_t selectorCount = 0;
712             char[42] nameString;
713             sprintf(nameString.ptr, "L_OBJC_SELECTOR_REFERENCES_%llu", cast(ulong) selectorCount);
714             auto symbol = symbol_name(nameString.ptr, SCstatic, type_fake(TYnptr));
715 
716             symbol.Sdt = dtb.finish();
717             symbol.Sseg = seg;
718             outdata(symbol);
719 
720             ++selectorCount;
721 
722             return symbol;
723         });
724     }
725 
726     Symbol* getMethVarRef(const Identifier ident)
727     {
728         return getMethVarRef(ident.toString());
729     }
730 
731     /**
732      * Returns the Objective-C type encoding for the given type.
733      *
734      * The available type encodings are documented by Apple, available at
735      * $(LINK2 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100, Type Encoding).
736      * The type encodings can also be obtained by running an Objective-C
737      * compiler and using the `@encode()` compiler directive.
738      *
739      * Params:
740      *  type = the type to return the type encoding for
741      *
742      * Returns: a string containing the type encoding
743      */
744     string getTypeEncoding(Type type)
745     in
746     {
747         assert(type !is null);
748     }
749     do
750     {
751         enum assertMessage = "imaginary types are not supported by Objective-C";
752 
753         with (ENUMTY) switch (type.ty)
754         {
755             case Tvoid: return "v";
756             case Tbool: return "B";
757             case Tint8: return "c";
758             case Tuns8: return "C";
759             case Tchar: return "C";
760             case Tint16: return "s";
761             case Tuns16: return "S";
762             case Twchar: return "S";
763             case Tint32: return "i";
764             case Tuns32: return "I";
765             case Tdchar: return "I";
766             case Tint64: return "q";
767             case Tuns64: return "Q";
768             case Tfloat32: return "f";
769             case Tcomplex32: return "jf";
770             case Tfloat64: return "d";
771             case Tcomplex64: return "jd";
772             case Tfloat80: return "D";
773             case Tcomplex80: return "jD";
774             case Timaginary32: assert(false, assertMessage);
775             case Timaginary64: assert(false, assertMessage);
776             case Timaginary80: assert(false, assertMessage);
777             default: return "?"; // unknown
778             // TODO: add "*" char*, "#" Class, "@" id, ":" SEL
779             // TODO: add "^"<type> indirection and "^^" double indirection
780         }
781     }
782 
783     /**
784      * Returns: the `L_OBJC_METH_VAR_TYPE_` symbol containing the given
785      * type encoding.
786      */
787     Symbol* getMethVarType(const(char)[] typeEncoding)
788     {
789         return cache(typeEncoding, methVarTypeTable, {
790             __gshared size_t count = 0;
791             char[42] nameString;
792             const symbolName = format(nameString, "L_OBJC_METH_VAR_TYPE_%lu", count++);
793             auto symbol = getCString(typeEncoding, symbolName, Segments.Id.methtype);
794 
795             outdata(symbol);
796 
797             return symbol;
798         });
799     }
800 
801     /// ditto
802     Symbol* getMethVarType(Type[] types ...)
803     {
804         string typeCode;
805         typeCode.reserve(types.length);
806 
807         foreach (type; types)
808             typeCode ~= getTypeEncoding(type);
809 
810         return getMethVarType(typeCode);
811     }
812 
813     /// ditto
814     Symbol* getMethVarType(FuncDeclaration func)
815     {
816         Type[] types = [func.type.nextOf]; // return type first
817 
818         if (func.parameters)
819         {
820             types.reserve(func.parameters.length);
821 
822             foreach (e; *func.parameters)
823                 types ~= e.type;
824         }
825 
826         return getMethVarType(types);
827     }
828 
829     /// Returns: the externally defined `__objc_empty_cache` symbol
830     Symbol* getEmptyCache()
831     {
832         return emptyCache = emptyCache ? emptyCache : getGlobal("__objc_empty_cache");
833     }
834 
835     /// Returns: the externally defined `__objc_empty_vtable` symbol
836     Symbol* getEmptyVTable()
837     {
838         return emptyVTable = emptyVTable ? emptyVTable : getGlobal("__objc_empty_vtable");
839     }
840 
841     /// Returns: the `L_OBJC_CLASS_NAME_` symbol for a class with the given name
842     Symbol* getClassNameRo(const(char)[] name)
843     {
844         return cache(name, classNameTable, {
845             __gshared size_t count = 0;
846             char[42] nameString;
847             const symbolName = format(nameString, "L_OBJC_CLASS_NAME_%lu", count++);
848 
849             return getCString(name, symbolName, Segments.Id.classname);
850         });
851     }
852 
853     /// ditto
854     Symbol* getClassNameRo(const Identifier ident)
855     {
856         return getClassNameRo(ident.toString());
857     }
858 
859     Symbol* getIVarOffset(ClassDeclaration cd, VarDeclaration var, bool outputSymbol)
860     {
861         hasSymbols_ = true;
862 
863         const className = cd.objc.identifier.toString;
864         const varName = var.ident.toString;
865         const name = "_OBJC_IVAR_$_" ~ className ~ '.' ~ varName;
866 
867         auto stringValue = ivarOffsetTable.update(name);
868         auto symbol = stringValue.value;
869 
870         if (!symbol)
871         {
872             symbol = getGlobal(name);
873             symbol.Sfl |= FLextern;
874             stringValue.value = symbol;
875         }
876 
877         if (!outputSymbol)
878             return symbol;
879 
880         auto dtb = DtBuilder(0);
881         dtb.size(var.offset);
882 
883         symbol.Sdt = dtb.finish();
884         symbol.Sseg = Segments[Segments.Id.ivar];
885         symbol.Sfl &= ~FLextern;
886 
887         outdata(symbol);
888 
889         return symbol;
890     }
891 
892     private Symbol* setMsgSendSymbol(string name)(tym_t ty = TYnfunc)
893     {
894         alias This = typeof(this);
895         enum fieldName = name[1 .. $];
896 
897         if (!__traits(getMember, This, fieldName))
898             __traits(getMember, This, fieldName) = getGlobal(name, type_fake(ty));
899 
900         return __traits(getMember, This, fieldName);
901     }
902 
903     /**
904      * Caches the symbol returned by `block` using the given name.
905      *
906      * If the symbol is already in the cache, the symbol will be returned
907      * immediately and `block` will not be called.
908      *
909      * Params:
910      *  name = the name to cache the symbol under
911      *  symbolCache = the cache storage to use for this symbol
912      *  block = invoked when the symbol is not in the cache. The return value
913      *      will be put into the cache
914      *
915      * Returns: the cached symbol
916      */
917     private Symbol* cache(const(char)[] name, SymbolCache symbolCache,
918         scope Symbol* delegate() block)
919     {
920         hasSymbols_ = true;
921 
922         auto stringValue = symbolCache.update(name);
923 
924         if (stringValue.value)
925             return stringValue.value;
926 
927         return stringValue.value = block();
928     }
929 }
930 
931 private:
932 
933 /**
934  * Functionality for outputting symbols for a specific Objective-C class
935  * declaration.
936  */
937 struct ObjcClassDeclaration
938 {
939     /// Indicates what kind of class this is.
940     private enum Flags
941     {
942         /// Regular class.
943         regular = 0x00000,
944 
945         /// Meta class.
946         meta = 0x00001,
947 
948         /// Root class. A class without any base class.
949         root = 0x00002
950     }
951 
952     /// The class declaration
953     ClassDeclaration classDeclaration;
954 
955     /// `true` if this class is a metaclass.
956     bool isMeta;
957 
958     this(ClassDeclaration classDeclaration, bool isMeta)
959     in
960     {
961         assert(classDeclaration !is null);
962     }
963     do
964     {
965         this.classDeclaration = classDeclaration;
966         this.isMeta = isMeta;
967     }
968 
969     /**
970      * Outputs the class declaration to the object file.
971      *
972      * Returns: the exported symbol, that is, `_OBJC_METACLASS_$_` or
973      * `_OBJC_CLASS_$_`
974      */
975     Symbol* toObjFile()
976     {
977         if (classDeclaration.objc.isExtern)
978             return null; // only a declaration for an externally-defined class
979 
980         auto dtb = DtBuilder(0);
981         toDt(dtb);
982 
983         auto symbol = Symbols.getClassName(this);
984         symbol.Sdt = dtb.finish();
985         symbol.Sseg = Segments[Segments.Id.data];
986         outdata(symbol);
987 
988         return symbol;
989     }
990 
991 private:
992 
993     /**
994      * Outputs the class declaration to the object file.
995      *
996      * Params:
997      *  dtb = the `DtBuilder` to output the class declaration to
998      */
999     void toDt(ref DtBuilder dtb)
1000     {
1001         auto baseClassSymbol = classDeclaration.baseClass ?
1002             Symbols.getClassName(classDeclaration.baseClass, isMeta) : null;
1003 
1004         dtb.xoff(getMetaclass(), 0); // pointer to metaclass
1005         dtb.xoffOrNull(baseClassSymbol); // pointer to base class
1006         dtb.xoff(Symbols.getEmptyCache(), 0);
1007         dtb.xoff(Symbols.getEmptyVTable(), 0);
1008         dtb.xoff(getClassRo(), 0);
1009     }
1010 
1011     /// Returns: the name of the metaclass of this class declaration
1012     Symbol* getMetaclass()
1013     {
1014         if (isMeta)
1015         {
1016             // metaclass: return root class's name
1017             // (will be replaced with metaclass reference at load)
1018 
1019             auto metaclassDeclaration = classDeclaration;
1020 
1021             while (metaclassDeclaration.baseClass)
1022                 metaclassDeclaration = metaclassDeclaration.baseClass;
1023 
1024             return Symbols.getClassName(metaclassDeclaration, true);
1025         }
1026 
1027         else
1028         {
1029             // regular class: return metaclass with the same name
1030             return ObjcClassDeclaration(classDeclaration, true).toObjFile();
1031         }
1032     }
1033 
1034     /**
1035      * Returns: the `l_OBJC_CLASS_RO_$_`/`l_OBJC_METACLASS_RO_$_` symbol for
1036      * this class declaration
1037      */
1038     Symbol* getClassRo()
1039     {
1040         auto dtb = DtBuilder(0);
1041 
1042         dtb.dword(flags);
1043         dtb.dword(instanceStart);
1044         dtb.dword(instanceSize);
1045         dtb.dword(0); // reserved
1046 
1047         dtb.size(0); // ivar layout
1048         dtb.xoff(Symbols.getClassNameRo(classDeclaration.ident), 0); // name of the class
1049 
1050         dtb.xoffOrNull(getMethodList()); // instance method list
1051         dtb.xoffOrNull(getProtocolList()); // protocol list
1052 
1053         if (isMeta)
1054         {
1055             dtb.size(0); // instance variable list
1056             dtb.size(0); // weak ivar layout
1057             dtb.size(0); // properties
1058         }
1059 
1060         else
1061         {
1062             dtb.xoffOrNull(getIVarList()); // instance variable list
1063             dtb.size(0); // weak ivar layout
1064             dtb.xoffOrNull(getPropertyList()); // properties
1065         }
1066 
1067         const prefix = isMeta ? "l_OBJC_METACLASS_RO_$_" : "l_OBJC_CLASS_RO_$_";
1068         const symbolName = prefix ~ classDeclaration.objc.identifier.toString();
1069         auto symbol = Symbols.getStatic(symbolName);
1070 
1071         symbol.Sdt = dtb.finish();
1072         symbol.Sseg = Segments[Segments.Id.const_];
1073         outdata(symbol);
1074 
1075         return symbol;
1076     }
1077 
1078     /**
1079      * Returns method list for this class declaration.
1080      *
1081      * This is a list of all methods defined in this class declaration, i.e.
1082      * methods with a body.
1083      *
1084      * Returns: the symbol for the method list, `l_OBJC_$_CLASS_METHODS_` or
1085      * `l_OBJC_$_INSTANCE_METHODS_`
1086      */
1087     Symbol* getMethodList()
1088     {
1089         /**
1090          * Returns the number of methods that should be added to the binary.
1091          *
1092          * Only methods with a body should be added.
1093          *
1094          * Params:
1095          *  members = the members of the class declaration
1096          */
1097         static int methodCount(FuncDeclarations* methods)
1098         {
1099             int count;
1100 
1101             methods.each!((method) {
1102                 if (method.fbody)
1103                     count++;
1104             });
1105 
1106             return count;
1107         }
1108 
1109         auto methods = isMeta ? classDeclaration.objc.metaclass.objc.methodList :
1110             classDeclaration.objc.methodList;
1111 
1112         const count = methodCount(methods);
1113 
1114         if (count == 0)
1115             return null;
1116 
1117         auto dtb = DtBuilder(0);
1118 
1119         dtb.dword(24); // _objc_method.sizeof
1120         dtb.dword(count); // method count
1121 
1122         methods.each!((func) {
1123             if (func.fbody)
1124             {
1125                 assert(func.objc.selector);
1126                 dtb.xoff(func.objc.selector.toNameSymbol(), 0); // method name
1127                 dtb.xoff(Symbols.getMethVarType(func), 0); // method type string
1128                 dtb.xoff(func.toSymbol(), 0); // function implementation
1129             }
1130         });
1131 
1132         const prefix = isMeta ? "l_OBJC_$_CLASS_METHODS_" : "l_OBJC_$_INSTANCE_METHODS_";
1133         const symbolName = prefix ~ classDeclaration.objc.identifier.toString();
1134         auto symbol = Symbols.getStatic(symbolName);
1135 
1136         symbol.Sdt = dtb.finish();
1137         symbol.Sseg = Segments[Segments.Id.const_];
1138 
1139         return symbol;
1140     }
1141 
1142     Symbol* getProtocolList()
1143     {
1144         // protocols are not supported yet
1145         return null;
1146     }
1147 
1148     Symbol* getIVarList()
1149     {
1150         if (isMeta || classDeclaration.fields.length == 0)
1151             return null;
1152 
1153         auto dtb = DtBuilder(0);
1154 
1155         dtb.dword(32); // entsize, _ivar_t.sizeof
1156         dtb.dword(cast(int) classDeclaration.fields.length); // ivar count
1157 
1158         foreach (field; classDeclaration.fields)
1159         {
1160             auto var = field.isVarDeclaration;
1161             assert(var);
1162             assert((var.storage_class & STC.static_) == 0);
1163 
1164             dtb.xoff(Symbols.getIVarOffset(classDeclaration, var, true), 0); // pointer to ivar offset
1165             dtb.xoff(Symbols.getMethVarName(var.ident), 0); // name
1166             dtb.xoff(Symbols.getMethVarType(var.type), 0); // type string
1167             dtb.dword(var.alignment);
1168             dtb.dword(cast(int) var.size(var.loc));
1169         }
1170 
1171         enum prefix = "l_OBJC_$_INSTANCE_VARIABLES_";
1172         const symbolName = prefix ~ classDeclaration.objc.identifier.toString();
1173         auto symbol = Symbols.getStatic(symbolName);
1174 
1175         symbol.Sdt = dtb.finish();
1176         symbol.Sseg = Segments[Segments.Id.const_];
1177 
1178         return symbol;
1179     }
1180 
1181     Symbol* getPropertyList()
1182     {
1183         // properties are not supported yet
1184         return null;
1185     }
1186 
1187     Symbol* getIVarOffset(VarDeclaration var)
1188     {
1189         if (var.toParent() is classDeclaration)
1190             return Symbols.getIVarOffset(classDeclaration, var, false);
1191 
1192         else if (classDeclaration.baseClass)
1193             return ObjcClassDeclaration(classDeclaration.baseClass, false)
1194                 .getIVarOffset(var);
1195 
1196         else
1197             assert(false, "Trying to get the base class of a root class");
1198     }
1199 
1200     /**
1201      * Returns the flags for this class declaration.
1202      *
1203      * That is, if this is a regular class, a metaclass and/or a root class.
1204      *
1205      * Returns: the flags
1206      */
1207     uint flags() const
1208     {
1209         uint flags = isMeta ? Flags.meta : Flags.regular;
1210 
1211         if (classDeclaration.objc.isRootClass)
1212             flags |= Flags.root;
1213 
1214         return flags;
1215     }
1216 
1217     /**
1218      * Returns the offset of where an instance of this class starts.
1219      *
1220      * For a metaclass this is always `40`. For a class with no instance
1221      * variables this is the size of the class declaration. For a class with
1222      * instance variables it's the offset of the first instance variable.
1223      *
1224      * Returns: the instance start
1225      */
1226     int instanceStart()
1227     {
1228         if (isMeta)
1229             return 40;
1230 
1231         const start = cast(uint) classDeclaration.size(classDeclaration.loc);
1232 
1233         if (!classDeclaration.members || classDeclaration.members.length == 0)
1234             return start;
1235 
1236         foreach (member; *classDeclaration.members)
1237         {
1238             auto var = member.isVarDeclaration;
1239 
1240             if (var && var.isField)
1241                 return var.offset;
1242         }
1243 
1244         return start;
1245     }
1246 
1247     /// Returns: the size of an instance of this class
1248     int instanceSize()
1249     {
1250         return isMeta ? 40 : cast(int) classDeclaration.size(classDeclaration.loc);
1251     }
1252 }
1253 
1254 /*
1255  * Formats the given arguments into the given buffer.
1256  *
1257  * Convenience wrapper around `snprintf`.
1258  *
1259  * Params:
1260  *  bufLength = length of the buffer
1261  *  buffer = the buffer where to store the result
1262  *  format = the format string
1263  *  args = the arguments to format
1264  *
1265  * Returns: the formatted result, a slice of the given buffer
1266  */
1267 char[] format(size_t bufLength, Args...)(return ref char[bufLength] buffer,
1268     const(char)* format, const Args args)
1269 {
1270     auto length = snprintf(buffer.ptr, buffer.length, format, args);
1271 
1272     assert(length >= 0, "An output error occurred");
1273     assert(length < buffer.length, "Output was truncated");
1274 
1275     return buffer[0 .. length];
1276 }
1277 
1278 /// Returns: the symbol of the given selector
1279 Symbol* toNameSymbol(const ObjcSelector* selector)
1280 {
1281     return Symbols.getMethVarName(selector.toString());
1282 }
1283 
1284 /**
1285  * Adds a reference to the given `symbol` or null if the symbol is null.
1286  *
1287  * Params:
1288  *  dtb = the dt builder to add the symbol to
1289  *  symbol = the symbol to add
1290  */
1291 void xoffOrNull(ref DtBuilder dtb, Symbol* symbol)
1292 {
1293     if (symbol)
1294         dtb.xoff(symbol, 0);
1295     else
1296         dtb.size(0);
1297 }
1298 
1299 /**
1300  * Converts the given D string to a null terminated C string.
1301  *
1302  * Asserts if `str` is longer than `maxLength`, with assertions enabled. With
1303  * assertions disabled it will truncate the result to `maxLength`.
1304  *
1305  * Params:
1306  *  maxLength = the max length of `str`
1307  *  str = the string to convert
1308  *  buf = the buffer where to allocate the result. By default this will be
1309  *      allocated in the caller scope using `alloca`. If the buffer is created
1310  *      by the callee it needs to be able to fit at least `str.length + 1` bytes
1311  *
1312  * Returns: the given string converted to a C string, a slice of `str` or the
1313  *  given buffer `buffer`
1314  */
1315 const(char)* toStringz(size_t maxLength = 4095)(in const(char)[] str,
1316     scope return void[] buffer = alloca(maxLength + 1)[0 .. maxLength + 1]) pure
1317 in
1318 {
1319     assert(maxLength >= str.length);
1320 }
1321 out(result)
1322 {
1323     assert(str.length == result.strlen);
1324 }
1325 do
1326 {
1327     if (str.length == 0)
1328         return "".ptr;
1329 
1330     const maxLength = buffer.length - 1;
1331     const len = str.length > maxLength ? maxLength : str.length;
1332     auto buf = cast(char[]) buffer[0 .. len + 1];
1333     buf[0 .. len] = str[0 .. len];
1334     buf[len] = '\0';
1335 
1336     return cast(const(char)*) buf.ptr;
1337 }