1
/**
2
 * Glue code for Objective-C interop.
3
 *
4
 * Copyright:   Copyright (C) 2015-2020 by The D Language Foundation, All Rights Reserved
5
 * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
6
 * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7
 * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/objc_glue.d, _objc_glue.d)
8
 * Documentation:  https://dlang.org/phobos/dmd_objc_glue.html
9
 * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/objc_glue.d
10
 */
11

12
module dmd.objc_glue;
13

14
import core.stdc.stdio;
15
import core.stdc.stdlib;
16
import core.stdc.string;
17

18
import dmd.aggregate;
19
import dmd.arraytypes;
20
import dmd.dclass;
21
import dmd.declaration;
22
import dmd.dmodule;
23
import dmd.dsymbol;
24
import dmd.expression;
25
import dmd.func;
26
import dmd.glue;
27
import dmd.identifier;
28
import dmd.mtype;
29
import dmd.objc;
30
import dmd.target;
31

32
import dmd.root.stringtable;
33

34
import dmd.backend.dt;
35
import dmd.backend.cc;
36
import dmd.backend.cdef;
37
import dmd.backend.el;
38
import dmd.backend.global;
39
import dmd.backend.oper;
40
import dmd.backend.outbuf;
41
import dmd.backend.ty;
42
import dmd.backend.type;
43
import dmd.backend.mach;
44
import dmd.backend.obj;
45

46
private __gshared ObjcGlue _objc;
47

48
ObjcGlue objc()
49
{
50 1
    return _objc;
51
}
52

53
// Should be an interface
54
extern(C++) abstract class ObjcGlue
55
{
56
    static struct ElemResult
57
    {
58
        elem* ec;
59
        elem* ethis;
60
    }
61

62
    static void initialize()
63
    {
64 1
        if (target.objc.supported)
65 0
            _objc = new Supported;
66
        else
67 1
            _objc = new Unsupported;
68
    }
69

70
    /// Resets the Objective-C glue layer.
71
    abstract void reset();
72

73
    abstract void setupMethodSelector(FuncDeclaration fd, elem** esel);
74

75
    abstract ElemResult setupMethodCall(FuncDeclaration fd, TypeFunction tf,
76
        bool directcall, elem* ec, elem* ehidden, elem* ethis);
77

78
    abstract void setupEp(elem* esel, elem** ep, int leftToRight);
79
    abstract void generateModuleInfo(Module module_);
80

81
    /// Returns: the given expression converted to an `elem` structure
82
    abstract elem* toElem(ObjcClassReferenceExp e) const;
83

84
    /// Outputs the given Objective-C class to the object file.
85
    abstract void toObjFile(ClassDeclaration classDeclaration) const;
86

87
    /**
88
     * Adds the selector parameter to the given list of parameters.
89
     *
90
     * For Objective-C methods the selector parameter is added. For
91
     * non-Objective-C methods `parameters` is unchanged.
92
     *
93
     * Params:
94
     *  functionDeclaration = the function declaration to add the selector
95
     *      parameter from
96
     *  parameters = the list of parameters to add the selector parameter to
97
     *  parameterCount = the number of parameters
98
     *
99
     * Returns: the new number of parameters
100
     */
101
    abstract size_t addSelectorParameterSymbol(
102
        FuncDeclaration functionDeclaration,
103
        Symbol** parameters, size_t parameterCount) const;
104

105
    /**
106
     * Returns the offset of the given variable declaration `var`.
107
     *
108
     * This is used in a `DotVarExp` to get the offset of the variable the
109
     * expression is accessing.
110
     *
111
     * Instance variables in Objective-C are non-fragile. That means that the
112
     * base class can change (add or remove instance variables) without the
113
     * subclasses needing to recompile or relink. This is implemented instance
114
     * variables having a dynamic offset. This is achieved by going through an
115
     * indirection in the form of a symbol generated in the binary. The compiler
116
     * outputs the static offset in the generated symbol. Then, at load time,
117
     * the symbol is updated with the correct offset, if necessary.
118
     *
119
     * Params:
120
     *  var = the variable declaration to return the offset of
121
     *  type = the type of the `DotVarExp`
122
     *  offset = the existing offset
123
     *
124
     * Returns: a symbol containing the offset of the variable declaration
125
     */
126
    abstract elem* getOffset(VarDeclaration var, Type type, elem* offset) const;
127
}
128

129
private:
130

131
extern(C++) final class Unsupported : ObjcGlue
132
{
133
    override void reset()
134
    {
135
        // noop
136
    }
137

138
    override void setupMethodSelector(FuncDeclaration fd, elem** esel)
139
    {
140
        // noop
141
    }
142

143
    override ElemResult setupMethodCall(FuncDeclaration, TypeFunction, bool,
144
        elem*, elem*, elem*)
145
    {
146 0
        assert(0, "Should never be called when Objective-C is not supported");
147
    }
148

149
    override void setupEp(elem* esel, elem** ep, int reverse)
150
    {
151
        // noop
152
    }
153

154
    override void generateModuleInfo(Module)
155
    {
156
        // noop
157
    }
158

159
    override elem* toElem(ObjcClassReferenceExp e) const
160
    {
161 0
        assert(0, "Should never be called when Objective-C is not supported");
162
    }
163

164
    override void toObjFile(ClassDeclaration classDeclaration) const
165
    {
166 0
        assert(0, "Should never be called when Objective-C is not supported");
167
    }
168

169
    override size_t addSelectorParameterSymbol(FuncDeclaration, Symbol**,
170
        size_t count) const
171
    {
172 1
        return count;
173
    }
174

175
    override elem* getOffset(VarDeclaration var, Type type, elem* offset) const
176
    {
177 1
        return offset;
178
    }
179
}
180

181
extern(C++) final class Supported : ObjcGlue
182
{
183 0
    extern (D) this()
184
    {
185 0
        Segments.initialize();
186 0
        Symbols.initialize();
187
    }
188

189
    override void reset()
190
    {
191 0
        Segments.reset();
192 0
        Symbols.reset();
193
    }
194

195
    override void setupMethodSelector(FuncDeclaration fd, elem** esel)
196
    {
197 0
        if (fd && fd.selector && !*esel)
198
        {
199 0
            *esel = el_var(Symbols.getMethVarRef(fd.selector.toString()));
200
        }
201
    }
202

203
    override ElemResult setupMethodCall(FuncDeclaration fd, TypeFunction tf,
204
        bool directcall, elem* ec, elem* ehidden, elem* ethis)
205
    {
206
        import dmd.e2ir : addressElem;
207

208 0
        if (directcall) // super call
209
        {
210 0
            ElemResult result;
211
            // call through Objective-C runtime dispatch
212 0
            result.ec = el_var(Symbols.getMsgSendSuper(ehidden !is null));
213

214
            // need to change this pointer to a pointer to an two-word
215
            // objc_super struct of the form { this ptr, class ptr }.
216 0
            auto cd = fd.isThis.isClassDeclaration;
217 0
            assert(cd, "call to objc_msgSendSuper with no class declaration");
218

219
            // faking objc_super type as delegate
220 0
            auto classRef = el_var(Symbols.getClassReference(cd));
221 0
            auto super_ = el_pair(TYdelegate, ethis, classRef);
222

223 0
            result.ethis = addressElem(super_, tf);
224

225 0
            return result;
226
        }
227

228
        else
229
        {
230
            // make objc-style "virtual" call using dispatch function
231 0
            assert(ethis);
232 0
            Type tret = tf.next;
233

234 0
            ElemResult result = {
235
                ec: el_var(Symbols.getMsgSend(tret, ehidden !is null)),
236
                ethis: ethis
237
            };
238

239 0
            return result;
240
        }
241
    }
242

243
    override void setupEp(elem* esel, elem** ep, int leftToRight)
244
    {
245 0
        if (esel)
246
        {
247
            // using objc-style "virtual" call
248
            // add hidden argument (second to 'this') for selector used by dispatch function
249 0
            if (leftToRight)
250 0
                *ep = el_param(esel, *ep);
251
            else
252 0
                *ep = el_param(*ep, esel);
253
        }
254
    }
255

256
    override void generateModuleInfo(Module module_)
257
    {
258 0
        ClassDeclarations classes;
259 0
        ClassDeclarations categories;
260

261 0
        module_.members.foreachDsymbol(m => m.addObjcSymbols(&classes, &categories));
262

263 0
        if (classes.length || categories.length || Symbols.hasSymbols)
264 0
            Symbols.getModuleInfo(classes, categories);
265
    }
266

267
    override elem* toElem(ObjcClassReferenceExp e) const
268
    {
269 0
        return el_var(Symbols.getClassReference(e.classDeclaration));
270
    }
271

272
    override void toObjFile(ClassDeclaration classDeclaration) const
273
    in
274
    {
275
        assert(classDeclaration !is null);
276
        assert(classDeclaration.classKind == ClassKind.objc);
277
    }
278
    do
279
    {
280 0
        if (!classDeclaration.objc.isMeta)
281 0
            ObjcClassDeclaration(classDeclaration, false).toObjFile();
282
    }
283

284
    override size_t addSelectorParameterSymbol(FuncDeclaration fd,
285
        Symbol** params, size_t count) const
286
    in
287
    {
288
        assert(fd);
289
    }
290
    do
291
    {
292 0
        if (!fd.selector)
293 0
            return count;
294

295 0
        assert(fd.selectorParameter);
296 0
        auto selectorSymbol = fd.selectorParameter.toSymbol();
297 0
        memmove(params + 1, params, count * params[0].sizeof);
298 0
        params[0] = selectorSymbol;
299

300 0
        return count + 1;
301
    }
302

303
    override elem* getOffset(VarDeclaration var, Type type, elem* offset) const
304
    {
305 0
        auto typeClass = type.isTypeClass;
306

307 0
        if (!typeClass || typeClass.sym.classKind != ClassKind.objc)
308 0
            return offset;
309

310 0
        return el_var(ObjcClassDeclaration(typeClass.sym, false).getIVarOffset(var));
311
    }
312
}
313

314
struct Segments
315
{
316
    enum Id
317
    {
318
        classlist,
319
        classname,
320
        classrefs,
321
        const_,
322
        data,
323
        imageinfo,
324
        ivar,
325
        methname,
326
        methtype,
327
        selrefs
328
    }
329

330
    private
331
    {
332
        __gshared int[Id] segments;
333
        __gshared Segments[Id] segmentData;
334

335
        immutable(char*) sectionName;
336
        immutable(char*) segmentName;
337
        immutable int flags;
338
        immutable int alignment;
339

340 0
        this(typeof(this.tupleof) tuple)
341
        {
342 0
            this.tupleof = tuple;
343
        }
344

345
        static void initialize()
346
        {
347 0
            segmentData = [
348
                Id.classlist: Segments("__objc_classlist", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 3),
349
                Id.classname: Segments("__objc_classname", "__TEXT", S_CSTRING_LITERALS, 0),
350
                Id.classrefs: Segments("__objc_classrefs", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 3),
351
                Id.const_: Segments("__objc_const", "__DATA", S_REGULAR, 3),
352
                Id.data: Segments("__objc_data", "__DATA", S_REGULAR, 3),
353
                Id.imageinfo: Segments("__objc_imageinfo", "__DATA", S_REGULAR | S_ATTR_NO_DEAD_STRIP, 0),
354
                Id.ivar: Segments("__objc_ivar", "__DATA", S_REGULAR, 3),
355
                Id.methname: Segments("__objc_methname", "__TEXT", S_CSTRING_LITERALS, 0),
356
                Id.methtype: Segments("__objc_methtype", "__TEXT", S_CSTRING_LITERALS, 0),
357
                Id.selrefs: Segments("__objc_selrefs", "__DATA", S_LITERAL_POINTERS | S_ATTR_NO_DEAD_STRIP, 3),
358
            ];
359
        }
360
    }
361

362
    /// Resets the segments.
363
    static void reset()
364
    {
365 0
        clearCache();
366
    }
367

368
    // Clears any caches.
369
    private static void clearCache()
370
    {
371 0
        segments.clear;
372
    }
373

374
    static int opIndex(Id id)
375
    {
376 0
        if (auto segment = id in segments)
377 0
            return *segment;
378

379 0
        const seg = segmentData[id];
380

381
        version (OSX)
382
        {
383
            return segments[id] = Obj.getsegment(
384
                seg.sectionName,
385
                seg.segmentName,
386
                seg.alignment,
387
                seg.flags
388
            );
389
        }
390

391
        else
392
        {
393
            // This should never happen. If the platform is not OSX an error
394
            // should have occurred sooner which should have prevented the
395
            // code from getting here.
396 0
            assert(0);
397
        }
398
    }
399
}
400

401
struct Symbols
402
{
403
static:
404

405
    private __gshared
406
    {
407
        alias SymbolCache = StringTable!(Symbol*)*;
408

409
        bool hasSymbols_ = false;
410

411
        Symbol* objc_msgSend = null;
412
        Symbol* objc_msgSend_stret = null;
413
        Symbol* objc_msgSend_fpret = null;
414
        Symbol* objc_msgSend_fp2ret = null;
415

416
        Symbol* objc_msgSendSuper = null;
417
        Symbol* objc_msgSendSuper_stret = null;
418

419
        Symbol* imageInfo = null;
420
        Symbol* moduleInfo = null;
421

422
        Symbol* emptyCache = null;
423
        Symbol* emptyVTable = null;
424

425
        // Cache for `_OBJC_METACLASS_$_`/`_OBJC_CLASS_$_` symbols.
426
        SymbolCache classNameTable = null;
427

428
        // Cache for `L_OBJC_CLASSLIST_REFERENCES_` symbols.
429
        SymbolCache classReferenceTable = null;
430

431
        SymbolCache methVarNameTable = null;
432
        SymbolCache methVarRefTable = null;
433
        SymbolCache methVarTypeTable = null;
434

435
        // Cache for instance variable offsets
436
        SymbolCache ivarOffsetTable = null;
437
    }
438

439
    void initialize()
440
    {
441 0
        initializeStringTables();
442
    }
443

444
    private void initializeStringTables()
445
    {
446
        alias This = typeof(this);
447

448
        foreach (m ; __traits(allMembers, This))
449
        {
450
            static if (is(typeof(__traits(getMember, This, m)) == SymbolCache))
451
            {
452 0
                __traits(getMember, This, m) = new StringTable!(Symbol*)();
453 0
                __traits(getMember, This, m)._init();
454
            }
455
        }
456
    }
457

458
    /// Resets the symbols.
459
    void reset()
460
    {
461 0
        clearCache();
462 0
        resetSymbolCache();
463
    }
464

465
    // Clears any caches.
466
    private void clearCache()
467
    {
468
        alias This = typeof(this);
469

470
        foreach (m ; __traits(allMembers, This))
471
        {
472
            static if (is(typeof(__traits(getMember, This, m)) == Symbol*))
473 0
                __traits(getMember, This, m) = null;
474
        }
475
    }
476

477
    // Resets the symbol caches.
478
    private void resetSymbolCache()
479
    {
480
        alias This = typeof(this);
481

482
        foreach (m ; __traits(allMembers, This))
483
        {
484
            static if (is(typeof(__traits(getMember, This, m)) == SymbolCache))
485 0
                __traits(getMember, This, m).reset();
486
        }
487
    }
488

489
    bool hasSymbols()
490
    {
491 0
        if (hasSymbols_)
492 0
            return true;
493

494
        alias This = typeof(this);
495

496
        foreach (m ; __traits(allMembers, This))
497
        {
498
            static if (is(typeof(__traits(getMember, This, m)) == Symbol*))
499
            {
500 0
                if (__traits(getMember, This, m) !is null)
501 0
                    return true;
502
            }
503
        }
504

505 0
        return false;
506
    }
507

508
    /**
509
     * Convenience wrapper around `dmd.backend.global.symbol_name`.
510
     *
511
     * Allows to pass the name of the symbol as a D string.
512
     */
513
    Symbol* symbolName(const(char)[] name, int sclass, type* t)
514
    {
515 0
        return symbol_name(name.ptr, cast(uint) name.length, sclass, t);
516
    }
517

518
    /**
519
     * Gets a global symbol.
520
     *
521
     * Params:
522
     *  name = the name of the symbol
523
     *  t = the type of the symbol
524
     *
525
     * Returns: the symbol
526
     */
527
    Symbol* getGlobal(const(char)[] name, type* t = type_fake(TYnptr))
528
    {
529 0
        return symbolName(name, SCglobal, t);
530
    }
531

532
    /**
533
     * Gets a static symbol.
534
     *
535
     * Params:
536
     *  name = the name of the symbol
537
     *  t = the type of the symbol
538
     *
539
     * Returns: the symbol
540
     */
541
    Symbol* getStatic(const(char)[] name, type* t = type_fake(TYnptr))
542
    {
543 0
        return symbolName(name, SCstatic, t);
544
    }
545

546
    Symbol* getCString(const(char)[] str, const(char)[] symbolName, Segments.Id segment)
547
    {
548 0
        hasSymbols_ = true;
549

550
        // create data
551 0
        auto dtb = DtBuilder(0);
552 0
        dtb.nbytes(cast(uint) (str.length + 1), str.toStringz());
553

554
        // find segment
555 0
        auto seg = Segments[segment];
556

557
        // create symbol
558 0
        auto s = getStatic(symbolName, type_allocn(TYarray, tstypes[TYchar]));
559 0
        s.Sdt = dtb.finish();
560 0
        s.Sseg = seg;
561 0
        return s;
562
    }
563

564
    Symbol* getMethVarName(const(char)[] name)
565
    {
566 0
        return cache(name, methVarNameTable, {
567 0
            __gshared size_t classNameCount = 0;
568 0
            char[42] buffer;
569 0
            const symbolName = format(buffer, "L_OBJC_METH_VAR_NAME_%lu", classNameCount++);
570

571 0
            return getCString(name, symbolName, Segments.Id.methname);
572
        });
573
    }
574

575
    Symbol* getMethVarName(Identifier ident)
576
    {
577 0
        return getMethVarName(ident.toString());
578
    }
579

580
    Symbol* getMsgSend(Type returnType, bool hasHiddenArgument)
581
    {
582 0
        if (hasHiddenArgument)
583 0
            return setMsgSendSymbol!("_objc_msgSend_stret")(TYhfunc);
584
        // not sure if DMD can handle this
585 0
        else if (returnType.ty == Tcomplex80)
586 0
            return setMsgSendSymbol!("_objc_msgSend_fp2ret");
587 0
        else if (returnType.ty == Tfloat80)
588 0
            return setMsgSendSymbol!("_objc_msgSend_fpret");
589
        else
590 0
            return setMsgSendSymbol!("_objc_msgSend");
591

592 0
        assert(0);
593
    }
594

595
    Symbol* getMsgSendSuper(bool hasHiddenArgument)
596
    {
597 0
        if (hasHiddenArgument)
598 0
            return setMsgSendSymbol!("_objc_msgSendSuper_stret")(TYhfunc);
599
        else
600 0
            return setMsgSendSymbol!("_objc_msgSendSuper")(TYnfunc);
601
    }
602

603
    Symbol* getImageInfo()
604
    {
605 0
        if (imageInfo)
606 0
            return imageInfo;
607

608 0
        auto dtb = DtBuilder(0);
609 0
        dtb.dword(0); // version
610 0
        dtb.dword(64); // flags
611

612 0
        imageInfo = symbol_name("L_OBJC_IMAGE_INFO", SCstatic, type_allocn(TYarray, tstypes[TYchar]));
613 0
        imageInfo.Sdt = dtb.finish();
614 0
        imageInfo.Sseg = Segments[Segments.Id.imageinfo];
615 0
        outdata(imageInfo);
616

617 0
        return imageInfo;
618
    }
619

620
    Symbol* getModuleInfo(/*const*/ ref ClassDeclarations classes,
621
        /*const*/ ref ClassDeclarations categories)
622
    {
623 0
        assert(!moduleInfo); // only allow once per object file
624

625 0
        auto dtb = DtBuilder(0);
626

627 0
        foreach (c; classes)
628 0
            dtb.xoff(getClassName(c), 0);
629

630 0
        foreach (c; categories)
631 0
            dtb.xoff(getClassName(c), 0);
632

633 0
        Symbol* symbol = symbol_name("L_OBJC_LABEL_CLASS_$", SCstatic, type_allocn(TYarray, tstypes[TYchar]));
634 0
        symbol.Sdt = dtb.finish();
635 0
        symbol.Sseg = Segments[Segments.Id.classlist];
636 0
        outdata(symbol);
637

638 0
        getImageInfo(); // make sure we also generate image info
639

640 0
        return moduleInfo;
641
    }
642

643
    /**
644
     * Returns: the `_OBJC_METACLASS_$_`/`_OBJC_CLASS_$_` symbol for the given
645
     *  class declaration.
646
     */
647
    Symbol* getClassName(ObjcClassDeclaration objcClass)
648
    {
649 0
        hasSymbols_ = true;
650

651 0
        const prefix = objcClass.isMeta ? "_OBJC_METACLASS_$_" : "_OBJC_CLASS_$_";
652 0
        auto name = prefix ~ objcClass.classDeclaration.objc.identifier.toString();
653

654 0
        return cache(name, classNameTable, () => getGlobal(name));
655
    }
656

657
    /// ditto
658
    Symbol* getClassName(ClassDeclaration classDeclaration, bool isMeta = false)
659
    in
660
    {
661 0
        assert(classDeclaration !is null);
662
    }
663
    do
664
    {
665 0
        return getClassName(ObjcClassDeclaration(classDeclaration, isMeta));
666
    }
667

668
    /*
669
     * Returns: the `L_OBJC_CLASSLIST_REFERENCES_$_` symbol for the given class
670
     *  declaration.
671
     */
672
    Symbol* getClassReference(ClassDeclaration classDeclaration)
673
    {
674 0
        hasSymbols_ = true;
675

676 0
        auto name = classDeclaration.objc.identifier.toString();
677

678 0
        return cache(name, classReferenceTable, {
679 0
            auto dtb = DtBuilder(0);
680 0
            auto className = getClassName(classDeclaration);
681 0
            dtb.xoff(className, 0, TYnptr);
682

683 0
            auto segment = Segments[Segments.Id.classrefs];
684

685 0
            __gshared size_t classReferenceCount = 0;
686

687 0
            char[42] nameString;
688 0
            auto result = format(nameString, "L_OBJC_CLASSLIST_REFERENCES_$_%lu", classReferenceCount++);
689 0
            auto symbol = getStatic(result);
690 0
            symbol.Sdt = dtb.finish();
691 0
            symbol.Sseg = segment;
692 0
            outdata(symbol);
693

694 0
            return symbol;
695
        });
696
    }
697

698
    Symbol* getMethVarRef(const(char)[] name)
699
    {
700 0
        return cache(name, methVarRefTable, {
701
            // create data
702 0
            auto dtb = DtBuilder(0);
703 0
            auto selector = getMethVarName(name);
704 0
            dtb.xoff(selector, 0, TYnptr);
705

706
            // find segment
707 0
            auto seg = Segments[Segments.Id.selrefs];
708

709
            // create symbol
710 0
            __gshared size_t selectorCount = 0;
711 0
            char[42] nameString;
712 0
            sprintf(nameString.ptr, "L_OBJC_SELECTOR_REFERENCES_%llu", cast(ulong) selectorCount);
713 0
            auto symbol = symbol_name(nameString.ptr, SCstatic, type_fake(TYnptr));
714

715 0
            symbol.Sdt = dtb.finish();
716 0
            symbol.Sseg = seg;
717 0
            outdata(symbol);
718

719 0
            ++selectorCount;
720

721 0
            return symbol;
722
        });
723
    }
724

725
    Symbol* getMethVarRef(const Identifier ident)
726
    {
727 0
        return getMethVarRef(ident.toString());
728
    }
729

730
    /**
731
     * Returns the Objective-C type encoding for the given type.
732
     *
733
     * The available type encodings are documented by Apple, available at
734
     * $(LINK2 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100, Type Encoding).
735
     * The type encodings can also be obtained by running an Objective-C
736
     * compiler and using the `@encode()` compiler directive.
737
     *
738
     * Params:
739
     *  type = the type to return the type encoding for
740
     *
741
     * Returns: a string containing the type encoding
742
     */
743
    string getTypeEncoding(Type type)
744
    in
745
    {
746 0
        assert(type !is null);
747
    }
748
    do
749
    {
750
        enum assertMessage = "imaginary types are not supported by Objective-C";
751

752 0
        with (ENUMTY) switch (type.ty)
753
        {
754 0
            case Tvoid: return "v";
755 0
            case Tbool: return "B";
756 0
            case Tint8: return "c";
757 0
            case Tuns8: return "C";
758 0
            case Tchar: return "C";
759 0
            case Tint16: return "s";
760 0
            case Tuns16: return "S";
761 0
            case Twchar: return "S";
762 0
            case Tint32: return "i";
763 0
            case Tuns32: return "I";
764 0
            case Tdchar: return "I";
765 0
            case Tint64: return "q";
766 0
            case Tuns64: return "Q";
767 0
            case Tfloat32: return "f";
768 0
            case Tcomplex32: return "jf";
769 0
            case Tfloat64: return "d";
770 0
            case Tcomplex64: return "jd";
771 0
            case Tfloat80: return "D";
772 0
            case Tcomplex80: return "jD";
773 0
            case Timaginary32: assert(false, assertMessage);
774 0
            case Timaginary64: assert(false, assertMessage);
775 0
            case Timaginary80: assert(false, assertMessage);
776 0
            default: return "?"; // unknown
777
            // TODO: add "*" char*, "#" Class, "@" id, ":" SEL
778
            // TODO: add "^"<type> indirection and "^^" double indirection
779
        }
780
    }
781

782
    /**
783
     * Returns: the `L_OBJC_METH_VAR_TYPE_` symbol containing the given
784
     * type encoding.
785
     */
786
    Symbol* getMethVarType(const(char)[] typeEncoding)
787
    {
788 0
        return cache(typeEncoding, methVarTypeTable, {
789 0
            __gshared size_t count = 0;
790 0
            char[42] nameString;
791 0
            const symbolName = format(nameString, "L_OBJC_METH_VAR_TYPE_%lu", count++);
792 0
            auto symbol = getCString(typeEncoding, symbolName, Segments.Id.methtype);
793

794 0
            outdata(symbol);
795

796 0
            return symbol;
797
        });
798
    }
799

800
    /// ditto
801
    Symbol* getMethVarType(Type[] types ...)
802
    {
803 0
        string typeCode;
804 0
        typeCode.reserve(types.length);
805

806 0
        foreach (type; types)
807 0
            typeCode ~= getTypeEncoding(type);
808

809 0
        return getMethVarType(typeCode);
810
    }
811

812
    /// ditto
813
    Symbol* getMethVarType(FuncDeclaration func)
814
    {
815 0
        Type[] types = [func.type.nextOf]; // return type first
816

817 0
        if (func.parameters)
818
        {
819 0
            types.reserve(func.parameters.length);
820

821 0
            foreach (e; *func.parameters)
822 0
                types ~= e.type;
823
        }
824

825 0
        return getMethVarType(types);
826
    }
827

828
    /// Returns: the externally defined `__objc_empty_cache` symbol
829
    Symbol* getEmptyCache()
830
    {
831 0
        return emptyCache = emptyCache ? emptyCache : getGlobal("__objc_empty_cache");
832
    }
833

834
    /// Returns: the externally defined `__objc_empty_vtable` symbol
835
    Symbol* getEmptyVTable()
836
    {
837 0
        return emptyVTable = emptyVTable ? emptyVTable : getGlobal("__objc_empty_vtable");
838
    }
839

840
    /// Returns: the `L_OBJC_CLASS_NAME_` symbol for a class with the given name
841
    Symbol* getClassNameRo(const(char)[] name)
842
    {
843 0
        return cache(name, classNameTable, {
844 0
            __gshared size_t count = 0;
845 0
            char[42] nameString;
846 0
            const symbolName = format(nameString, "L_OBJC_CLASS_NAME_%lu", count++);
847

848 0
            return getCString(name, symbolName, Segments.Id.classname);
849
        });
850
    }
851

852
    /// ditto
853
    Symbol* getClassNameRo(const Identifier ident)
854
    {
855 0
        return getClassNameRo(ident.toString());
856
    }
857

858
    Symbol* getIVarOffset(ClassDeclaration cd, VarDeclaration var, bool outputSymbol)
859
    {
860 0
        hasSymbols_ = true;
861

862 0
        const className = cd.objc.identifier.toString;
863 0
        const varName = var.ident.toString;
864 0
        const name = "_OBJC_IVAR_$_" ~ className ~ '.' ~ varName;
865

866 0
        auto stringValue = ivarOffsetTable.update(name);
867 0
        auto symbol = stringValue.value;
868

869 0
        if (!symbol)
870
        {
871 0
            symbol = getGlobal(name);
872 0
            symbol.Sfl |= FLextern;
873 0
            stringValue.value = symbol;
874
        }
875

876 0
        if (!outputSymbol)
877 0
            return symbol;
878

879 0
        auto dtb = DtBuilder(0);
880 0
        dtb.size(var.offset);
881

882 0
        symbol.Sdt = dtb.finish();
883 0
        symbol.Sseg = Segments[Segments.Id.ivar];
884 0
        symbol.Sfl &= ~FLextern;
885

886 0
        outdata(symbol);
887

888 0
        return symbol;
889
    }
890

891
    private Symbol* setMsgSendSymbol(string name)(tym_t ty = TYnfunc)
892
    {
893
        alias This = typeof(this);
894
        enum fieldName = name[1 .. $];
895

896 0
        if (!__traits(getMember, This, fieldName))
897 0
            __traits(getMember, This, fieldName) = getGlobal(name, type_fake(ty));
898

899 0
        return __traits(getMember, This, fieldName);
900
    }
901

902
    /**
903
     * Caches the symbol returned by `block` using the given name.
904
     *
905
     * If the symbol is already in the cache, the symbol will be returned
906
     * immediately and `block` will not be called.
907
     *
908
     * Params:
909
     *  name = the name to cache the symbol under
910
     *  symbolCache = the cache storage to use for this symbol
911
     *  block = invoked when the symbol is not in the cache. The return value
912
     *      will be put into the cache
913
     *
914
     * Returns: the cached symbol
915
     */
916
    private Symbol* cache(const(char)[] name, SymbolCache symbolCache,
917
        scope Symbol* delegate() block)
918
    {
919 0
        hasSymbols_ = true;
920

921 0
        auto stringValue = symbolCache.update(name);
922

923 0
        if (stringValue.value)
924 0
            return stringValue.value;
925

926 0
        return stringValue.value = block();
927
    }
928
}
929

930
private:
931

932
/**
933
 * Functionality for outputting symbols for a specific Objective-C class
934
 * declaration.
935
 */
936
struct ObjcClassDeclaration
937
{
938
    /// Indicates what kind of class this is.
939
    private enum Flags
940
    {
941
        /// Regular class.
942
        regular = 0x00000,
943

944
        /// Meta class.
945
        meta = 0x00001,
946

947
        /// Root class. A class without any base class.
948
        root = 0x00002
949
    }
950

951
    /// The class declaration
952
    ClassDeclaration classDeclaration;
953

954
    /// `true` if this class is a metaclass.
955
    bool isMeta;
956

957 0
    this(ClassDeclaration classDeclaration, bool isMeta)
958
    in
959
    {
960 0
        assert(classDeclaration !is null);
961
    }
962
    do
963
    {
964 0
        this.classDeclaration = classDeclaration;
965 0
        this.isMeta = isMeta;
966
    }
967

968
    /**
969
     * Outputs the class declaration to the object file.
970
     *
971
     * Returns: the exported symbol, that is, `_OBJC_METACLASS_$_` or
972
     * `_OBJC_CLASS_$_`
973
     */
974
    Symbol* toObjFile()
975
    {
976 0
        if (classDeclaration.objc.isExtern)
977 0
            return null; // only a declaration for an externally-defined class
978

979 0
        auto dtb = DtBuilder(0);
980 0
        toDt(dtb);
981

982 0
        auto symbol = Symbols.getClassName(this);
983 0
        symbol.Sdt = dtb.finish();
984 0
        symbol.Sseg = Segments[Segments.Id.data];
985 0
        outdata(symbol);
986

987 0
        return symbol;
988
    }
989

990
private:
991

992
    /**
993
     * Outputs the class declaration to the object file.
994
     *
995
     * Params:
996
     *  dtb = the `DtBuilder` to output the class declaration to
997
     */
998
    void toDt(ref DtBuilder dtb)
999
    {
1000 0
        auto baseClassSymbol = classDeclaration.baseClass ?
1001 0
            Symbols.getClassName(classDeclaration.baseClass, isMeta) : null;
1002

1003 0
        dtb.xoff(getMetaclass(), 0); // pointer to metaclass
1004 0
        dtb.xoffOrNull(baseClassSymbol); // pointer to base class
1005 0
        dtb.xoff(Symbols.getEmptyCache(), 0);
1006 0
        dtb.xoff(Symbols.getEmptyVTable(), 0);
1007 0
        dtb.xoff(getClassRo(), 0);
1008
    }
1009

1010
    /// Returns: the name of the metaclass of this class declaration
1011
    Symbol* getMetaclass()
1012
    {
1013 0
        if (isMeta)
1014
        {
1015
            // metaclass: return root class's name
1016
            // (will be replaced with metaclass reference at load)
1017

1018 0
            auto metaclassDeclaration = classDeclaration;
1019

1020 0
            while (metaclassDeclaration.baseClass)
1021 0
                metaclassDeclaration = metaclassDeclaration.baseClass;
1022

1023 0
            return Symbols.getClassName(metaclassDeclaration, true);
1024
        }
1025

1026
        else
1027
        {
1028
            // regular class: return metaclass with the same name
1029 0
            return ObjcClassDeclaration(classDeclaration, true).toObjFile();
1030
        }
1031
    }
1032

1033
    /**
1034
     * Returns: the `l_OBJC_CLASS_RO_$_`/`l_OBJC_METACLASS_RO_$_` symbol for
1035
     * this class declaration
1036
     */
1037
    Symbol* getClassRo()
1038
    {
1039 0
        auto dtb = DtBuilder(0);
1040

1041 0
        dtb.dword(flags);
1042 0
        dtb.dword(instanceStart);
1043 0
        dtb.dword(instanceSize);
1044 0
        dtb.dword(0); // reserved
1045

1046 0
        dtb.size(0); // ivar layout
1047 0
        dtb.xoff(Symbols.getClassNameRo(classDeclaration.ident), 0); // name of the class
1048

1049 0
        dtb.xoffOrNull(getMethodList()); // instance method list
1050 0
        dtb.xoffOrNull(getProtocolList()); // protocol list
1051

1052 0
        if (isMeta)
1053
        {
1054 0
            dtb.size(0); // instance variable list
1055 0
            dtb.size(0); // weak ivar layout
1056 0
            dtb.size(0); // properties
1057
        }
1058

1059
        else
1060
        {
1061 0
            dtb.xoffOrNull(getIVarList()); // instance variable list
1062 0
            dtb.size(0); // weak ivar layout
1063 0
            dtb.xoffOrNull(getPropertyList()); // properties
1064
        }
1065

1066 0
        const prefix = isMeta ? "l_OBJC_METACLASS_RO_$_" : "l_OBJC_CLASS_RO_$_";
1067 0
        const symbolName = prefix ~ classDeclaration.objc.identifier.toString();
1068 0
        auto symbol = Symbols.getStatic(symbolName);
1069

1070 0
        symbol.Sdt = dtb.finish();
1071 0
        symbol.Sseg = Segments[Segments.Id.const_];
1072 0
        outdata(symbol);
1073

1074 0
        return symbol;
1075
    }
1076

1077
    /**
1078
     * Returns method list for this class declaration.
1079
     *
1080
     * This is a list of all methods defined in this class declaration, i.e.
1081
     * methods with a body.
1082
     *
1083
     * Returns: the symbol for the method list, `l_OBJC_$_CLASS_METHODS_` or
1084
     * `l_OBJC_$_INSTANCE_METHODS_`
1085
     */
1086
    Symbol* getMethodList()
1087
    {
1088
        /**
1089
         * Returns the number of methods that should be added to the binary.
1090
         *
1091
         * Only methods with a body should be added.
1092
         *
1093
         * Params:
1094
         *  members = the members of the class declaration
1095
         */
1096
        static int methodCount(Dsymbols* members)
1097
        {
1098 0
            int count;
1099

1100 0
            members.foreachDsymbol((member) {
1101 0
                const func = member.isFuncDeclaration;
1102

1103 0
                if (func && func.fbody)
1104 0
                    count++;
1105
            });
1106

1107 0
            return count;
1108
        }
1109

1110 0
        auto methods = isMeta ? classDeclaration.objc.metaclass.objc.methodList :
1111 0
            classDeclaration.objc.methodList;
1112

1113 0
        const count = methodCount(methods);
1114

1115 0
        if (count == 0)
1116 0
            return null;
1117

1118 0
        auto dtb = DtBuilder(0);
1119

1120 0
        dtb.dword(24); // _objc_method.sizeof
1121 0
        dtb.dword(count); // method count
1122

1123 0
        methods.foreachDsymbol((method) {
1124 0
            auto func = method.isFuncDeclaration;
1125

1126 0
            if (func && func.fbody)
1127
            {
1128 0
                assert(func.selector);
1129 0
                dtb.xoff(func.selector.toNameSymbol(), 0); // method name
1130 0
                dtb.xoff(Symbols.getMethVarType(func), 0); // method type string
1131 0
                dtb.xoff(func.toSymbol(), 0); // function implementation
1132
            }
1133
        });
1134

1135 0
        const prefix = isMeta ? "l_OBJC_$_CLASS_METHODS_" : "l_OBJC_$_INSTANCE_METHODS_";
1136 0
        const symbolName = prefix ~ classDeclaration.objc.identifier.toString();
1137 0
        auto symbol = Symbols.getStatic(symbolName);
1138

1139 0
        symbol.Sdt = dtb.finish();
1140 0
        symbol.Sseg = Segments[Segments.Id.const_];
1141

1142 0
        return symbol;
1143
    }
1144

1145
    Symbol* getProtocolList()
1146
    {
1147
        // protocols are not supported yet
1148 0
        return null;
1149
    }
1150

1151
    Symbol* getIVarList()
1152
    {
1153 0
        if (isMeta || classDeclaration.fields.length == 0)
1154 0
            return null;
1155

1156 0
        auto dtb = DtBuilder(0);
1157

1158 0
        dtb.dword(32); // entsize, _ivar_t.sizeof
1159 0
        dtb.dword(cast(int) classDeclaration.fields.length); // ivar count
1160

1161 0
        foreach (field; classDeclaration.fields)
1162
        {
1163 0
            auto var = field.isVarDeclaration;
1164 0
            assert(var);
1165 0
            assert((var.storage_class & STC.static_) == 0);
1166

1167 0
            dtb.xoff(Symbols.getIVarOffset(classDeclaration, var, true), 0); // pointer to ivar offset
1168 0
            dtb.xoff(Symbols.getMethVarName(var.ident), 0); // name
1169 0
            dtb.xoff(Symbols.getMethVarType(var.type), 0); // type string
1170 0
            dtb.dword(var.alignment);
1171 0
            dtb.dword(cast(int) var.size(var.loc));
1172
        }
1173

1174
        enum prefix = "l_OBJC_$_INSTANCE_VARIABLES_";
1175 0
        const symbolName = prefix ~ classDeclaration.objc.identifier.toString();
1176 0
        auto symbol = Symbols.getStatic(symbolName);
1177

1178 0
        symbol.Sdt = dtb.finish();
1179 0
        symbol.Sseg = Segments[Segments.Id.const_];
1180

1181 0
        return symbol;
1182
    }
1183

1184
    Symbol* getPropertyList()
1185
    {
1186
        // properties are not supported yet
1187 0
        return null;
1188
    }
1189

1190
    Symbol* getIVarOffset(VarDeclaration var)
1191
    {
1192 0
        if (var.toParent() is classDeclaration)
1193 0
            return Symbols.getIVarOffset(classDeclaration, var, false);
1194

1195 0
        else if (classDeclaration.baseClass)
1196 0
            return ObjcClassDeclaration(classDeclaration.baseClass, false)
1197
                .getIVarOffset(var);
1198

1199
        else
1200 0
            assert(false, "Trying to get the base class of a root class");
1201
    }
1202

1203
    /**
1204
     * Returns the flags for this class declaration.
1205
     *
1206
     * That is, if this is a regular class, a metaclass and/or a root class.
1207
     *
1208
     * Returns: the flags
1209
     */
1210
    uint flags() const
1211
    {
1212 0
        uint flags = isMeta ? Flags.meta : Flags.regular;
1213

1214 0
        if (classDeclaration.objc.isRootClass)
1215 0
            flags |= Flags.root;
1216

1217 0
        return flags;
1218
    }
1219

1220
    /**
1221
     * Returns the offset of where an instance of this class starts.
1222
     *
1223
     * For a metaclass this is always `40`. For a class with no instance
1224
     * variables this is the size of the class declaration. For a class with
1225
     * instance variables it's the offset of the first instance variable.
1226
     *
1227
     * Returns: the instance start
1228
     */
1229
    int instanceStart()
1230
    {
1231 0
        if (isMeta)
1232 0
            return 40;
1233

1234 0
        const start = cast(uint) classDeclaration.size(classDeclaration.loc);
1235

1236 0
        if (!classDeclaration.members || classDeclaration.members.length == 0)
1237 0
            return start;
1238

1239 0
        foreach (member; *classDeclaration.members)
1240
        {
1241 0
            auto var = member.isVarDeclaration;
1242

1243 0
            if (var && var.isField)
1244 0
                return var.offset;
1245
        }
1246

1247 0
        return start;
1248
    }
1249

1250
    /// Returns: the size of an instance of this class
1251
    int instanceSize()
1252
    {
1253 0
        return isMeta ? 40 : cast(int) classDeclaration.size(classDeclaration.loc);
1254
    }
1255
}
1256

1257
/*
1258
 * Formats the given arguments into the given buffer.
1259
 *
1260
 * Convenience wrapper around `snprintf`.
1261
 *
1262
 * Params:
1263
 *  bufLength = length of the buffer
1264
 *  buffer = the buffer where to store the result
1265
 *  format = the format string
1266
 *  args = the arguments to format
1267
 *
1268
 * Returns: the formatted result, a slice of the given buffer
1269
 */
1270
char[] format(size_t bufLength, Args...)(return ref char[bufLength] buffer,
1271
    const(char)* format, const Args args)
1272
{
1273 0
    auto length = snprintf(buffer.ptr, buffer.length, format, args);
1274

1275 0
    assert(length >= 0, "An output error occurred");
1276 0
    assert(length < buffer.length, "Output was truncated");
1277

1278 0
    return buffer[0 .. length];
1279
}
1280

1281
/// Returns: the symbol of the given selector
1282
Symbol* toNameSymbol(const ObjcSelector* selector)
1283
{
1284 0
    return Symbols.getMethVarName(selector.toString());
1285
}
1286

1287
/**
1288
 * Adds a reference to the given `symbol` or null if the symbol is null.
1289
 *
1290
 * Params:
1291
 *  dtb = the dt builder to add the symbol to
1292
 *  symbol = the symbol to add
1293
 */
1294
void xoffOrNull(ref DtBuilder dtb, Symbol* symbol)
1295
{
1296 0
    if (symbol)
1297 0
        dtb.xoff(symbol, 0);
1298
    else
1299 0
        dtb.size(0);
1300
}
1301

1302
/**
1303
 * Converts the given D string to a null terminated C string.
1304
 *
1305
 * Asserts if `str` is longer than `maxLength`, with assertions enabled. With
1306
 * assertions disabled it will truncate the result to `maxLength`.
1307
 *
1308
 * Params:
1309
 *  maxLength = the max length of `str`
1310
 *  str = the string to convert
1311
 *  buf = the buffer where to allocate the result. By default this will be
1312
 *      allocated in the caller scope using `alloca`. If the buffer is created
1313
 *      by the callee it needs to be able to fit at least `str.length + 1` bytes
1314
 *
1315
 * Returns: the given string converted to a C string, a slice of `str` or the
1316
 *  given buffer `buffer`
1317
 */
1318
const(char)* toStringz(size_t maxLength = 4095)(in const(char)[] str,
1319
    scope return void[] buffer = alloca(maxLength + 1)[0 .. maxLength + 1]) pure
1320
in
1321
{
1322 0
    assert(maxLength >= str.length);
1323
}
1324
out(result)
1325 0
{
1326 0
    assert(str.length == result.strlen);
1327
}
1328
do
1329
{
1330 0
    if (str.length == 0)
1331 0
        return "".ptr;
1332

1333 0
    const maxLength = buffer.length - 1;
1334 0
    const len = str.length > maxLength ? maxLength : str.length;
1335 0
    auto buf = cast(char[]) buffer[0 .. len + 1];
1336 0
    buf[0 .. len] = str[0 .. len];
1337 0
    buf[len] = '\0';
1338

1339 0
    return cast(const(char)*) buf.ptr;
1340
}

Read our documentation on viewing source code .

Loading