1/**
2 * Implementation of array assignment support routines.
3 *
4 * Copyright: Copyright Digital Mars 2004 - 2010.
5 * License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 * Authors:   Walter Bright, Sean Kelly
7 * Source: $(DRUNTIMESRC rt/_cast_.d)
8 */
9
10/*          Copyright Digital Mars 2004 - 2010.
11 * Distributed under the Boost Software License, Version 1.0.
12 *    (See accompanying file LICENSE or copy at
13 *          http://www.boost.org/LICENSE_1_0.txt)
14 */
15module rt.cast_;
16
17extern (C):
18@nogc:
19nothrow:
20pure:
21
22// Needed because ClassInfo.opEquals(Object) does a dynamic cast,
23// but we are trying to implement dynamic cast.
24extern (D) private bool areClassInfosEqual(scope const ClassInfo a, scope const ClassInfo b) @safe
25{
26    if (a is b)
27        return true;
28    // take care of potential duplicates across binaries
29    return a.name == b.name;
30}
31
32/******************************************
33 * Given a pointer:
34 *      If it is an Object, return that Object.
35 *      If it is an interface, return the Object implementing the interface.
36 *      If it is null, return null.
37 *      Else, undefined crash
38 */
39Object _d_toObject(return scope void* p)
40{
41    if (!p)
42        return null;
43
44    Object o = cast(Object) p;
45    ClassInfo oc = typeid(o);
46    Interface* pi = **cast(Interface***) p;
47
48    /* Interface.offset lines up with ClassInfo.name.ptr,
49     * so we rely on pointers never being less than 64K,
50     * and Objects never being greater.
51     */
52    if (pi.offset < 0x10000)
53    {
54        debug(cast_) printf("\tpi.offset = %d\n", pi.offset);
55        return cast(Object)(p - pi.offset);
56    }
57    return o;
58}
59
60/*************************************
61 * Attempts to cast Object o to class c.
62 * Returns o if successful, null if not.
63 */
64void* _d_interface_cast(void* p, ClassInfo c)
65{
66    debug(cast_) printf("_d_interface_cast(p = %p, c = '%.*s')\n", p, c.name);
67    if (!p)
68        return null;
69
70    Interface* pi = **cast(Interface***) p;
71
72    debug(cast_) printf("\tpi.offset = %d\n", pi.offset);
73    return _d_dynamic_cast(cast(Object)(p - pi.offset), c);
74}
75
76void* _d_dynamic_cast(Object o, ClassInfo c)
77{
78    debug(cast_) printf("_d_dynamic_cast(o = %p, c = '%.*s')\n", o, c.name);
79
80    void* res = null;
81    size_t offset = 0;
82    if (o && _d_isbaseof2(typeid(o), c, offset))
83    {
84        debug(cast_) printf("\toffset = %d\n", offset);
85        res = cast(void*) o + offset;
86    }
87    debug(cast_) printf("\tresult = %p\n", res);
88    return res;
89}
90
91int _d_isbaseof2(scope ClassInfo oc, scope const ClassInfo c, scope ref size_t offset) @safe
92{
93    if (areClassInfosEqual(oc, c))
94        return true;
95
96    do
97    {
98        if (oc.base && areClassInfosEqual(oc.base, c))
99            return true;
100
101        // Bugzilla 2013: Use depth-first search to calculate offset
102        // from the derived (oc) to the base (c).
103        foreach (iface; oc.interfaces)
104        {
105            if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof2(iface.classinfo, c, offset))
106            {
107                offset += iface.offset;
108                return true;
109            }
110        }
111
112        oc = oc.base;
113    } while (oc);
114
115    return false;
116}
117
118int _d_isbaseof(scope ClassInfo oc, scope const ClassInfo c) @safe
119{
120    if (areClassInfosEqual(oc, c))
121        return true;
122
123    do
124    {
125        if (oc.base && areClassInfosEqual(oc.base, c))
126            return true;
127
128        foreach (iface; oc.interfaces)
129        {
130            if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof(iface.classinfo, c))
131                return true;
132        }
133
134        oc = oc.base;
135    } while (oc);
136
137    return false;
138}
139