1
/**
2
 * Interfacing with Objective-C.
3
 *
4
 * Specification: $(LINK2 https://dlang.org/spec/objc_interface.html, Interfacing to Objective-C)
5
 *
6
 * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
7
 * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8
 * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9
 * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/objc.d, _objc.d)
10
 * Documentation:  https://dlang.org/phobos/dmd_objc.html
11
 * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/objc.d
12
 */
13

14
module dmd.objc;
15

16
import dmd.aggregate;
17
import dmd.arraytypes;
18
import dmd.attrib;
19
import dmd.cond;
20
import dmd.dclass;
21
import dmd.declaration;
22
import dmd.denum;
23
import dmd.dmangle;
24
import dmd.dmodule;
25
import dmd.dscope;
26
import dmd.dstruct;
27
import dmd.dsymbol;
28
import dmd.dsymbolsem;
29
import dmd.errors;
30
import dmd.expression;
31
import dmd.expressionsem;
32
import dmd.func;
33
import dmd.globals;
34
import dmd.gluelayer;
35
import dmd.hdrgen;
36
import dmd.id;
37
import dmd.identifier;
38
import dmd.mtype;
39
import dmd.root.array;
40
import dmd.root.outbuffer;
41
import dmd.root.stringtable;
42
import dmd.target;
43
import dmd.tokens;
44

45
struct ObjcSelector
46
{
47
    // MARK: Selector
48
    private __gshared StringTable!(ObjcSelector*) stringtable;
49
    private __gshared int incnum = 0;
50
    const(char)* stringvalue;
51
    size_t stringlen;
52
    size_t paramCount;
53

54
    extern (C++) static void _init()
55
    {
56 1
        stringtable._init();
57
    }
58

59 1
    extern (D) this(const(char)* sv, size_t len, size_t pcount)
60
    {
61 1
        stringvalue = sv;
62 1
        stringlen = len;
63 1
        paramCount = pcount;
64
    }
65

66
    extern (D) static ObjcSelector* lookup(const(char)* s)
67
    {
68 1
        size_t len = 0;
69 1
        size_t pcount = 0;
70 1
        const(char)* i = s;
71 1
        while (*i != 0)
72
        {
73 1
            ++len;
74 1
            if (*i == ':')
75 1
                ++pcount;
76 1
            ++i;
77
        }
78 1
        return lookup(s, len, pcount);
79
    }
80

81
    extern (D) static ObjcSelector* lookup(const(char)* s, size_t len, size_t pcount)
82
    {
83 1
        auto sv = stringtable.update(s, len);
84 1
        ObjcSelector* sel = sv.value;
85 1
        if (!sel)
86
        {
87 1
            sel = new ObjcSelector(sv.toDchars(), len, pcount);
88 1
            sv.value = sel;
89
        }
90 1
        return sel;
91
    }
92

93
    extern (C++) static ObjcSelector* create(FuncDeclaration fdecl)
94
    {
95 0
        OutBuffer buf;
96 0
        TypeFunction ftype = cast(TypeFunction)fdecl.type;
97 0
        const id = fdecl.ident.toString();
98 0
        const nparams = ftype.parameterList.length;
99
        // Special case: property setter
100 0
        if (ftype.isproperty && nparams == 1)
101
        {
102
            // rewrite "identifier" as "setIdentifier"
103 0
            char firstChar = id[0];
104 0
            if (firstChar >= 'a' && firstChar <= 'z')
105 0
                firstChar = cast(char)(firstChar - 'a' + 'A');
106 0
            buf.writestring("set");
107 0
            buf.writeByte(firstChar);
108 0
            buf.write(id[1 .. id.length - 1]);
109 0
            buf.writeByte(':');
110 0
            goto Lcomplete;
111
        }
112
        // write identifier in selector
113 0
        buf.write(id[]);
114
        // add mangled type and colon for each parameter
115 0
        if (nparams)
116
        {
117 0
            buf.writeByte('_');
118 0
            foreach (i, fparam; ftype.parameterList)
119
            {
120 0
                mangleToBuffer(fparam.type, &buf);
121 0
                buf.writeByte(':');
122
            }
123
        }
124
    Lcomplete:
125 0
        buf.writeByte('\0');
126
        // the slice is not expected to include a terminating 0
127 0
        return lookup(cast(const(char)*)buf[].ptr, buf.length - 1, nparams);
128
    }
129

130
    extern (D) const(char)[] toString() const pure
131
    {
132 1
        return stringvalue[0 .. stringlen];
133
    }
134
}
135

136
private __gshared Objc _objc;
137

138
Objc objc()
139
{
140 5
    return _objc;
141
}
142

143

144
/**
145
 * Contains all data for a class declaration that is needed for the Objective-C
146
 * integration.
147
 */
148
extern (C++) struct ObjcClassDeclaration
149
{
150
    /// `true` if this class is a metaclass.
151
    bool isMeta = false;
152

153
    /// `true` if this class is externally defined.
154
    bool isExtern = false;
155

156
    /// Name of this class.
157
    Identifier identifier;
158

159
    /// The class declaration this belongs to.
160
    ClassDeclaration classDeclaration;
161

162
    /// The metaclass of this class.
163
    ClassDeclaration metaclass;
164

165
    /// List of non-inherited methods.
166
    FuncDeclaration[] methodList;
167

168 5
    extern (D) this(ClassDeclaration classDeclaration)
169
    {
170 5
        this.classDeclaration = classDeclaration;
171
    }
172

173
    bool isRootClass() const
174
    {
175 1
        return classDeclaration.classKind == ClassKind.objc &&
176 1
            !metaclass &&
177 0
            !classDeclaration.baseClass;
178
    }
179
}
180

181
/**
182
 * Contains all data for a function declaration that is needed for the
183
 * Objective-C integration.
184
 */
185
extern (C++) struct ObjcFuncDeclaration
186
{
187
    /// The method selector (member functions only).
188
    ObjcSelector* selector;
189

190
    /// The implicit selector parameter.
191
    VarDeclaration selectorParameter;
192

193
    /// `true` if this function declaration is declared optional.
194
    bool isOptional;
195
}
196

197
// Should be an interface
198
extern(C++) abstract class Objc
199
{
200
    static void _init()
201
    {
202 5
        if (target.objc.supported)
203 1
            _objc = new Supported;
204
        else
205 5
            _objc = new Unsupported;
206
    }
207

208
    /**
209
     * Deinitializes the global state of the compiler.
210
     *
211
     * This can be used to restore the state set by `_init` to its original
212
     * state.
213
     */
214
    static void deinitialize()
215
    {
216 0
        _objc = _objc.init;
217
    }
218

219
    abstract void setObjc(ClassDeclaration cd);
220
    abstract void setObjc(InterfaceDeclaration);
221

222
    /**
223
     * Returns a pretty textual representation of the given class declaration.
224
     *
225
     * Params:
226
     *  classDeclaration = the class declaration to return the textual representation for
227
     *  qualifyTypes = `true` if types should be qualified in the result
228
     *
229
     * Returns: the textual representation
230
     */
231
    abstract const(char)* toPrettyChars(ClassDeclaration classDeclaration, bool qualifyTypes) const;
232

233
    abstract void setSelector(FuncDeclaration, Scope* sc);
234
    abstract void validateSelector(FuncDeclaration fd);
235
    abstract void checkLinkage(FuncDeclaration fd);
236

237
    /**
238
     * Returns `true` if the given function declaration is virtual.
239
     *
240
     * Function declarations with Objective-C linkage and which are static or
241
     * final are considered virtual.
242
     *
243
     * Params:
244
     *  fd = the function declaration to check if it's virtual
245
     *
246
     * Returns: `true` if the given function declaration is virtual
247
     */
248
    abstract bool isVirtual(const FuncDeclaration fd) const;
249

250
    /**
251
     * Marks the given function declaration as optional.
252
     *
253
     * A function declaration is considered optional if it's annotated with the
254
     * UDA: `@(core.attribute.optional)`. Only function declarations inside
255
     * interface declarations and with Objective-C linkage can be declared as
256
     * optional.
257
     *
258
     * Params:
259
     *  functionDeclaration = the function declaration to be set as optional
260
     *  sc = the scope from the semantic phase
261
     */
262
    abstract void setAsOptional(FuncDeclaration functionDeclaration, Scope* sc) const;
263

264
    /**
265
     * Validates function declarations declared optional.
266
     *
267
     * Params:
268
     *  functionDeclaration = the function declaration to validate
269
     */
270
    abstract void validateOptional(FuncDeclaration functionDeclaration) const;
271

272
    /**
273
     * Gets the parent of the given function declaration.
274
     *
275
     * Handles Objective-C static member functions, which are virtual functions
276
     * of the metaclass, by returning the parent class declaration to the
277
     * metaclass.
278
     *
279
     * Params:
280
     *  fd = the function declaration to get the parent of
281
     *  cd = the current parent, i.e. the class declaration the given function
282
     *      declaration belongs to
283
     *
284
     * Returns: the parent
285
     */
286
    abstract ClassDeclaration getParent(FuncDeclaration fd,
287
        ClassDeclaration cd) const;
288

289
    /**
290
     * Adds the given function to the list of Objective-C methods.
291
     *
292
     * This list will later be used output the necessary Objective-C module info.
293
     *
294
     * Params:
295
     *  fd = the function declaration to be added to the list
296
     *  cd = the class declaration the function belongs to
297
     */
298
    abstract void addToClassMethodList(FuncDeclaration fd,
299
        ClassDeclaration cd) const;
300

301
    /**
302
     * Returns the `this` pointer of the given function declaration.
303
     *
304
     * This is only used for class/static methods. For instance methods, no
305
     * Objective-C specialization is necessary.
306
     *
307
     * Params:
308
     *  funcDeclaration = the function declaration to get the `this` pointer for
309
     *
310
     * Returns: the `this` pointer of the given function declaration, or `null`
311
     *  if the given function declaration is not an Objective-C method.
312
     */
313
    abstract inout(AggregateDeclaration) isThis(inout FuncDeclaration funcDeclaration) const;
314

315
    /**
316
     * Creates the selector parameter for the given function declaration.
317
     *
318
     * Objective-C methods has an extra hidden parameter that comes after the
319
     * `this` parameter. The selector parameter is of the Objective-C type `SEL`
320
     * and contains the selector which this method was called with.
321
     *
322
     * Params:
323
     *  fd = the function declaration to create the parameter for
324
     *  sc = the scope from the semantic phase
325
     *
326
     * Returns: the newly created selector parameter or `null` for
327
     *  non-Objective-C functions
328
     */
329
    abstract VarDeclaration createSelectorParameter(FuncDeclaration fd, Scope* sc) const;
330

331
    /**
332
     * Creates and sets the metaclass on the given class/interface declaration.
333
     *
334
     * Will only be performed on regular Objective-C classes, not on metaclasses.
335
     *
336
     * Params:
337
     *  classDeclaration = the class/interface declaration to set the metaclass on
338
     */
339
    abstract void setMetaclass(InterfaceDeclaration interfaceDeclaration, Scope* sc) const;
340

341
    /// ditto
342
    abstract void setMetaclass(ClassDeclaration classDeclaration, Scope* sc) const;
343

344
    /**
345
     * Returns Objective-C runtime metaclass of the given class declaration.
346
     *
347
     * `ClassDeclaration.ObjcClassDeclaration.metaclass` contains the metaclass
348
     * from the semantic point of view. This function returns the metaclass from
349
     * the Objective-C runtime's point of view. Here, the metaclass of a
350
     * metaclass is the root metaclass, not `null`. The root metaclass's
351
     * metaclass is itself.
352
     *
353
     * Params:
354
     *  classDeclaration = The class declaration to return the metaclass of
355
     *
356
     * Returns: the Objective-C runtime metaclass of the given class declaration
357
     */
358
    abstract ClassDeclaration getRuntimeMetaclass(ClassDeclaration classDeclaration) const;
359

360
    ///
361
    abstract void addSymbols(AttribDeclaration attribDeclaration,
362
        ClassDeclarations* classes, ClassDeclarations* categories) const;
363

364
    ///
365
    abstract void addSymbols(ClassDeclaration classDeclaration,
366
        ClassDeclarations* classes, ClassDeclarations* categories) const;
367

368
    /**
369
     * Issues a compile time error if the `.offsetof`/`.tupleof` property is
370
     * used on a field of an Objective-C class.
371
     *
372
     * To solve the fragile base class problem in Objective-C, fields have a
373
     * dynamic offset instead of a static offset. The compiler outputs a
374
     * statically known offset which later the dynamic loader can update, if
375
     * necessary, when the application is loaded. Due to this behavior it
376
     * doesn't make sense to be able to get the offset of a field at compile
377
     * time, because this offset might not actually be the same at runtime.
378
     *
379
     * To get the offset of a field that is correct at runtime, functionality
380
     * from the Objective-C runtime can be used instead.
381
     *
382
     * Params:
383
     *  expression = the `.offsetof`/`.tupleof` expression
384
     *  aggregateDeclaration = the aggregate declaration the field of the
385
     *      `.offsetof`/`.tupleof` expression belongs to
386
     *  type = the type of the receiver of the `.tupleof` expression
387
     *
388
     * See_Also:
389
     *  $(LINK2 https://en.wikipedia.org/wiki/Fragile_binary_interface_problem,
390
     *      Fragile Binary Interface Problem)
391
     *
392
     * See_Also:
393
     *  $(LINK2 https://developer.apple.com/documentation/objectivec/objective_c_runtime,
394
     *      Objective-C Runtime)
395
     */
396
    abstract void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const;
397

398
    /// ditto
399
    abstract void checkTupleof(Expression expression, TypeClass type) const;
400
}
401

402
extern(C++) private final class Unsupported : Objc
403
{
404 5
    extern(D) final this()
405
    {
406 5
        ObjcGlue.initialize();
407
    }
408

409
    override void setObjc(ClassDeclaration cd)
410
    {
411 0
        cd.error("Objective-C classes not supported");
412
    }
413

414
    override void setObjc(InterfaceDeclaration id)
415
    {
416 0
        id.error("Objective-C interfaces not supported");
417
    }
418

419
    override const(char)* toPrettyChars(ClassDeclaration, bool qualifyTypes) const
420
    {
421 0
        assert(0, "Should never be called when Objective-C is not supported");
422
    }
423

424
    override void setSelector(FuncDeclaration, Scope*)
425
    {
426
        // noop
427
    }
428

429
    override void validateSelector(FuncDeclaration)
430
    {
431
        // noop
432
    }
433

434
    override void checkLinkage(FuncDeclaration)
435
    {
436
        // noop
437
    }
438

439
    override bool isVirtual(const FuncDeclaration) const
440
    {
441 0
        assert(0, "Should never be called when Objective-C is not supported");
442
    }
443

444
    override void setAsOptional(FuncDeclaration, Scope*) const
445
    {
446
        // noop
447
    }
448

449
    override void validateOptional(FuncDeclaration) const
450
    {
451
        // noop
452
    }
453

454
    override ClassDeclaration getParent(FuncDeclaration, ClassDeclaration cd) const
455
    {
456 5
        return cd;
457
    }
458

459
    override void addToClassMethodList(FuncDeclaration, ClassDeclaration) const
460
    {
461
        // noop
462
    }
463

464
    override inout(AggregateDeclaration) isThis(inout FuncDeclaration funcDeclaration) const
465
    {
466 5
        return null;
467
    }
468

469
    override VarDeclaration createSelectorParameter(FuncDeclaration, Scope*) const
470
    {
471 4
        return null;
472
    }
473

474
    override void setMetaclass(InterfaceDeclaration, Scope*) const
475
    {
476
        // noop
477
    }
478

479
    override void setMetaclass(ClassDeclaration, Scope*) const
480
    {
481
        // noop
482
    }
483

484
    override ClassDeclaration getRuntimeMetaclass(ClassDeclaration classDeclaration) const
485
    {
486 0
        assert(0, "Should never be called when Objective-C is not supported");
487
    }
488

489
    override void addSymbols(AttribDeclaration attribDeclaration,
490
        ClassDeclarations* classes, ClassDeclarations* categories) const
491
    {
492
        // noop
493
    }
494

495
    override void addSymbols(ClassDeclaration classDeclaration,
496
        ClassDeclarations* classes, ClassDeclarations* categories) const
497
    {
498
        // noop
499
    }
500

501
    override void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const
502
    {
503
        // noop
504
    }
505

506
    override void checkTupleof(Expression expression, TypeClass type) const
507
    {
508
        // noop
509
    }
510
}
511

512
extern(C++) private final class Supported : Objc
513
{
514 1
    extern(D) final this()
515
    {
516 1
        VersionCondition.addPredefinedGlobalIdent("D_ObjectiveC");
517

518 1
        ObjcGlue.initialize();
519 1
        ObjcSelector._init();
520
    }
521

522
    override void setObjc(ClassDeclaration cd)
523
    {
524 1
        cd.classKind = ClassKind.objc;
525 1
        cd.objc.isExtern = (cd.storage_class & STC.extern_) > 0;
526
    }
527

528
    override void setObjc(InterfaceDeclaration id)
529
    {
530 1
        id.classKind = ClassKind.objc;
531 1
        id.objc.isExtern = true;
532
    }
533

534
    override const(char)* toPrettyChars(ClassDeclaration cd, bool qualifyTypes) const
535
    {
536 1
        return cd.parent.toPrettyChars(qualifyTypes);
537
    }
538

539
    override void setSelector(FuncDeclaration fd, Scope* sc)
540
    {
541 1
        foreachUda(fd, sc, (e) {
542 1
            if (e.op != TOK.structLiteral)
543 1
                return 0;
544

545 1
            auto literal = cast(StructLiteralExp) e;
546 1
            assert(literal.sd);
547

548 1
            if (!isCoreUda(literal.sd, Id.udaSelector))
549 1
                return 0;
550

551 1
            if (fd.objc.selector)
552
            {
553 0
                fd.error("can only have one Objective-C selector per method");
554 0
                return 1;
555
            }
556

557 1
            assert(literal.elements.dim == 1);
558 1
            auto se = (*literal.elements)[0].toStringExp();
559 1
            assert(se);
560

561 1
            fd.objc.selector = ObjcSelector.lookup(se.toUTF8(sc).peekString().ptr);
562

563 1
            return 0;
564
        });
565
    }
566

567
    override void validateSelector(FuncDeclaration fd)
568
    {
569 1
        if (!fd.objc.selector)
570 1
            return;
571 1
        TypeFunction tf = cast(TypeFunction)fd.type;
572 1
        if (fd.objc.selector.paramCount != tf.parameterList.parameters.dim)
573 0
            fd.error("number of colons in Objective-C selector must match number of parameters");
574 1
        if (fd.parent && fd.parent.isTemplateInstance())
575 0
            fd.error("template cannot have an Objective-C selector attached");
576
    }
577

578
    override void checkLinkage(FuncDeclaration fd)
579
    {
580 1
        if (fd.linkage != LINK.objc && fd.objc.selector)
581 0
            fd.error("must have Objective-C linkage to attach a selector");
582
    }
583

584
    override bool isVirtual(const FuncDeclaration fd) const
585
    in
586
    {
587
        assert(fd.selector);
588
        assert(fd.isMember);
589
    }
590
    do
591
    {
592 1
        if (fd.toParent.isInterfaceDeclaration && fd.isFinal)
593 1
            return false;
594

595
        // * final member functions are kept virtual with Objective-C linkage
596
        //   because the Objective-C runtime always use dynamic dispatch.
597
        // * static member functions are kept virtual too, as they represent
598
        //   methods of the metaclass.
599 1
        with (fd.visibility)
600 1
            return !(kind == Visibility.Kind.private_ || kind == Visibility.Kind.package_);
601
    }
602

603
    override void setAsOptional(FuncDeclaration fd, Scope* sc) const
604
    {
605 1
        const count = declaredAsOptionalCount(fd, sc);
606 1
        fd.objc.isOptional = count > 0;
607

608 1
        if (count > 1)
609 0
            fd.error("can only declare a function as optional once");
610
    }
611

612
    /// Returns: the number of times `fd` has been declared as optional.
613
    private int declaredAsOptionalCount(FuncDeclaration fd , Scope* sc) const
614
    {
615 1
        int count;
616

617 1
        foreachUda(fd, sc, (e) {
618 1
            if (e.op != TOK.type)
619 1
                return 0;
620

621 1
            auto typeExp = cast(TypeExp) e;
622

623 1
            if (typeExp.type.ty != Tenum)
624 0
                return 0;
625

626 1
            auto typeEnum = cast(TypeEnum) typeExp.type;
627

628 1
            if (isCoreUda(typeEnum.sym, Id.udaOptional))
629 1
                count++;
630

631 1
            return 0;
632
        });
633

634 1
        return count;
635
    }
636

637
    override void validateOptional(FuncDeclaration fd) const
638
    {
639 1
        if (!fd.objc.isOptional)
640 1
            return;
641

642 1
        if (fd.linkage != LINK.objc)
643
        {
644 0
            fd.error("only functions with Objective-C linkage can be declared as optional");
645

646 0
            const linkage = linkageToString(fd.linkage);
647

648 0
            errorSupplemental(fd.loc, "function is declared with %.*s linkage",
649
                cast(uint) linkage.length, linkage.ptr);
650
        }
651

652 1
        auto parent = fd.parent;
653

654 1
        if (parent && parent.isTemplateInstance())
655
        {
656 0
            fd.error("template cannot be optional");
657 0
            parent = parent.parent;
658 0
            assert(parent);
659
        }
660

661 1
        if (parent && !parent.isInterfaceDeclaration())
662
        {
663 0
            fd.error("only functions declared inside interfaces can be optional");
664 0
            errorSupplemental(fd.loc, "function is declared inside %s", fd.parent.kind);
665
        }
666
    }
667

668
    override ClassDeclaration getParent(FuncDeclaration fd, ClassDeclaration cd) const
669
    out(metaclass)
670 1
    {
671 1
        assert(metaclass);
672
    }
673
    do
674
    {
675 1
        if (cd.classKind == ClassKind.objc && fd.isStatic && !cd.objc.isMeta)
676 1
            return cd.objc.metaclass;
677
        else
678 1
            return cd;
679
    }
680

681
    override void addToClassMethodList(FuncDeclaration fd, ClassDeclaration cd) const
682
    in
683
    {
684
        assert(fd.parent.isClassDeclaration);
685
    }
686
    do
687
    {
688 1
        if (cd.classKind != ClassKind.objc)
689 1
            return;
690

691 1
        if (!fd.objc.selector)
692 1
            return;
693

694 1
        assert(fd.isStatic ? cd.objc.isMeta : !cd.objc.isMeta);
695

696 1
        cd.objc.methodList ~= fd;
697
    }
698

699
    override inout(AggregateDeclaration) isThis(inout FuncDeclaration funcDeclaration) const
700
    {
701 1
        with(funcDeclaration)
702
        {
703 1
            if (!objc.selector)
704 1
                return null;
705

706
            // Use Objective-C class object as 'this'
707 1
            auto cd = isMember2().isClassDeclaration();
708

709 1
            if (cd.classKind == ClassKind.objc)
710
            {
711 1
                if (!cd.objc.isMeta)
712 1
                    return cd.objc.metaclass;
713
            }
714

715 0
            return null;
716
        }
717
    }
718

719
    override VarDeclaration createSelectorParameter(FuncDeclaration fd, Scope* sc) const
720
    in
721
    {
722
        assert(fd.selectorParameter is null);
723
    }
724
    do
725
    {
726 1
        if (!fd.objc.selector)
727 1
            return null;
728

729 1
        auto ident = Identifier.generateAnonymousId("_cmd");
730 1
        auto var = new VarDeclaration(fd.loc, Type.tvoidptr, ident, null);
731 1
        var.storage_class |= STC.parameter;
732 1
        var.dsymbolSemantic(sc);
733 1
        if (!sc.insert(var))
734 0
            assert(false);
735 1
        var.parent = fd;
736

737 1
        return var;
738
    }
739

740
    override void setMetaclass(InterfaceDeclaration interfaceDeclaration, Scope* sc) const
741
    {
742
        auto newMetaclass(Loc loc, BaseClasses* metaBases)
743
        {
744 1
            auto ident = createMetaclassIdentifier(interfaceDeclaration);
745 1
            return new InterfaceDeclaration(loc, ident, metaBases);
746
        }
747

748 1
        .setMetaclass!newMetaclass(interfaceDeclaration, sc);
749
    }
750

751
    override void setMetaclass(ClassDeclaration classDeclaration, Scope* sc) const
752
    {
753
        auto newMetaclass(Loc loc, BaseClasses* metaBases)
754
        {
755 1
            auto ident = createMetaclassIdentifier(classDeclaration);
756 1
            return new ClassDeclaration(loc, ident, metaBases, new Dsymbols(), 0);
757
        }
758

759 1
        .setMetaclass!newMetaclass(classDeclaration, sc);
760
    }
761

762
    override ClassDeclaration getRuntimeMetaclass(ClassDeclaration classDeclaration) const
763
    {
764 1
        if (!classDeclaration.objc.metaclass && classDeclaration.objc.isMeta)
765
        {
766 0
            if (classDeclaration.baseClass)
767 0
                return getRuntimeMetaclass(classDeclaration.baseClass);
768
            else
769 0
                return classDeclaration;
770
        }
771
        else
772 1
            return classDeclaration.objc.metaclass;
773
    }
774

775
    override void addSymbols(AttribDeclaration attribDeclaration,
776
        ClassDeclarations* classes, ClassDeclarations* categories) const
777
    {
778 1
        auto symbols = attribDeclaration.include(null);
779

780 1
        if (!symbols)
781 1
            return;
782

783 1
        foreach (symbol; *symbols)
784 1
            symbol.addObjcSymbols(classes, categories);
785
    }
786

787
    override void addSymbols(ClassDeclaration classDeclaration,
788
        ClassDeclarations* classes, ClassDeclarations* categories) const
789
    {
790 1
        with (classDeclaration)
791 1
            if (classKind == ClassKind.objc && !objc.isExtern && !objc.isMeta)
792 1
                classes.push(classDeclaration);
793
    }
794

795
    override void checkOffsetof(Expression expression, AggregateDeclaration aggregateDeclaration) const
796
    {
797 1
        if (aggregateDeclaration.classKind != ClassKind.objc)
798 1
            return;
799

800
        enum errorMessage = "no property `offsetof` for member `%s` of type " ~
801
            "`%s`";
802

803
        enum supplementalMessage = "`offsetof` is not available for members " ~
804
            "of Objective-C classes. Please use the Objective-C runtime instead";
805

806 0
        expression.error(errorMessage, expression.toChars(),
807
            expression.type.toChars());
808 0
        expression.errorSupplemental(supplementalMessage);
809
    }
810

811
    override void checkTupleof(Expression expression, TypeClass type) const
812
    {
813 1
        if (type.sym.classKind != ClassKind.objc)
814 1
            return;
815

816 0
        expression.error("no property `tupleof` for type `%s`", type.toChars());
817 0
        expression.errorSupplemental("`tupleof` is not available for members " ~
818
            "of Objective-C classes. Please use the Objective-C runtime instead");
819
    }
820

821
extern(D) private:
822

823
    /**
824
     * Returns `true` if the given symbol is a symbol declared in
825
     * `core.attribute` and has the given identifier.
826
     *
827
     * This is used to determine if a symbol is a UDA declared in
828
     * `core.attribute`.
829
     *
830
     * Params:
831
     *  sd = the symbol to check
832
     *  ident = the name of the expected UDA
833
     */
834
    bool isCoreUda(ScopeDsymbol sd, Identifier ident) const
835
    {
836 1
        if (sd.ident != ident || !sd.parent)
837 1
            return false;
838

839 1
        auto _module = sd.parent.isModule();
840 1
        return _module && _module.isCoreModule(Id.attribute);
841
    }
842

843
    /**
844
     * Iterates the UDAs attached to the given function declaration.
845
     *
846
     * If `dg` returns `!= 0`, it will stop the iteration and return that
847
     * value, otherwise it will return 0.
848
     *
849
     * Params:
850
     *  fd = the function declaration to get the UDAs from
851
     *  dg = called once for each UDA. If `dg` returns `!= 0`, it will stop the
852
     *      iteration and return that value, otherwise it will return `0`.
853
     */
854
    int foreachUda(FuncDeclaration fd, Scope* sc, int delegate(Expression) dg) const
855
    {
856 1
        if (!fd.userAttribDecl)
857 1
            return 0;
858

859 1
        auto udas = fd.userAttribDecl.getAttributes();
860 1
        arrayExpressionSemantic(udas, sc, true);
861

862 1
        return udas.each!((uda) {
863 1
            if (uda.op != TOK.tuple)
864 0
                return 0;
865

866 1
            auto exps = (cast(TupleExp) uda).exps;
867

868 1
            return exps.each!((e) {
869 1
                assert(e);
870

871 1
                if (auto result = dg(e))
872 0
                    return result;
873

874 1
                return 0;
875
            });
876
        });
877
    }
878
}
879

880
/*
881
 * Creates and sets the metaclass on the given class/interface declaration.
882
 *
883
 * Will only be performed on regular Objective-C classes, not on metaclasses.
884
 *
885
 * Params:
886
 *  newMetaclass = a function that returns the metaclass to set. This should
887
 *      return the same type as `T`.
888
 *  classDeclaration = the class/interface declaration to set the metaclass on
889
 */
890
private void setMetaclass(alias newMetaclass, T)(T classDeclaration, Scope* sc)
891
if (is(T == ClassDeclaration) || is(T == InterfaceDeclaration))
892
{
893
    static if (is(T == ClassDeclaration))
894
        enum errorType = "class";
895
    else
896
        enum errorType = "interface";
897

898 1
    with (classDeclaration)
899
    {
900 1
        if (classKind != ClassKind.objc || objc.isMeta || objc.metaclass)
901 1
            return;
902

903 1
        if (!objc.identifier)
904 1
            objc.identifier = classDeclaration.ident;
905

906 1
        auto metaBases = new BaseClasses();
907

908 1
        foreach (base ; baseclasses.opSlice)
909
        {
910 1
            auto baseCd = base.sym;
911 1
            assert(baseCd);
912

913 1
            if (baseCd.classKind == ClassKind.objc)
914
            {
915 1
                assert(baseCd.objc.metaclass);
916 1
                assert(baseCd.objc.metaclass.objc.isMeta);
917 1
                assert(baseCd.objc.metaclass.type.ty == Tclass);
918

919 1
                auto metaBase = new BaseClass(baseCd.objc.metaclass.type);
920 1
                metaBase.sym = baseCd.objc.metaclass;
921 1
                metaBases.push(metaBase);
922
            }
923
            else
924
            {
925 0
                error("base " ~ errorType ~ " for an Objective-C " ~
926
                      errorType ~ " must be `extern (Objective-C)`");
927
            }
928
        }
929

930 1
        objc.metaclass = newMetaclass(loc, metaBases);
931 1
        objc.metaclass.storage_class |= STC.static_;
932 1
        objc.metaclass.classKind = ClassKind.objc;
933 1
        objc.metaclass.objc.isMeta = true;
934 1
        objc.metaclass.objc.isExtern = objc.isExtern;
935 1
        objc.metaclass.objc.identifier = objc.identifier;
936

937 1
        if (baseClass)
938 1
            objc.metaclass.baseClass = baseClass.objc.metaclass;
939

940 1
        members.push(objc.metaclass);
941 1
        objc.metaclass.addMember(sc, classDeclaration);
942

943 1
        objc.metaclass.members = new Dsymbols();
944 1
        objc.metaclass.dsymbolSemantic(sc);
945
    }
946
}
947

948
private Identifier createMetaclassIdentifier(ClassDeclaration classDeclaration)
949
{
950 1
    const name = "class_" ~ classDeclaration.ident.toString ~ "_Meta";
951 1
    return Identifier.generateAnonymousId(name);
952
}

Read our documentation on viewing source code .

Loading