207 lines
5.3 KiB
D
207 lines
5.3 KiB
D
module core.internal.cast_;
|
|
|
|
// Needed because ClassInfo.opEquals(Object) does a dynamic cast,
|
|
// but we are trying to implement dynamic cast.
|
|
bool areClassInfosEqual(scope const ClassInfo a, scope const ClassInfo b) pure nothrow @safe @nogc
|
|
{
|
|
// same class if signatures match, works with potential duplicates across binaries
|
|
if (a is b)
|
|
return true;
|
|
|
|
// new fast way
|
|
if (a.m_flags & TypeInfo_Class.ClassFlags.hasNameSig)
|
|
return a.nameSig[0] == b.nameSig[0]
|
|
&& a.nameSig[1] == b.nameSig[1]
|
|
&& a.nameSig[2] == b.nameSig[2]
|
|
&& a.nameSig[3] == b.nameSig[3];
|
|
|
|
// old slow way for temporary binary compatibility
|
|
return a.name == b.name;
|
|
}
|
|
|
|
|
|
/*****
|
|
* Dynamic cast from a class object `o` to class or interface `To`, where `To` is a subtype of `o`.
|
|
* Params:
|
|
* o = instance of class
|
|
* To = class or interface that is a subtype of `o`
|
|
* Returns:
|
|
* null if `o` is null or `To` is not a subclass type of `o`. Otherwise, return `o`.
|
|
*/
|
|
private void* _d_dynamic_cast(To)(const return scope Object o) @trusted
|
|
{
|
|
void* res = null;
|
|
size_t offset = 0;
|
|
|
|
if (o && _d_isbaseof2!To(typeid(o), offset))
|
|
{
|
|
res = cast(void*) o + offset;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Dynamic cast `o` to final class `To` only one level down
|
|
* Params:
|
|
* o = object that is instance of a class
|
|
* To = final class that is a subclass type of `o`
|
|
* Returns:
|
|
* o if it succeeds, null if it fails
|
|
*/
|
|
private void* _d_paint_cast(To)(const return scope Object o)
|
|
{
|
|
/* If o is really an instance of c, just do a paint
|
|
*/
|
|
auto p = o && cast(void*)(areClassInfosEqual(typeid(o), typeid(To).info)) ? o : null;
|
|
debug assert(cast(void*)p is cast(void*)_d_dynamic_cast!To(o));
|
|
return cast(void*)p;
|
|
}
|
|
|
|
private void* _d_class_cast_impl(const return scope Object o, const ClassInfo c) pure nothrow @safe @nogc
|
|
{
|
|
if (!o)
|
|
return null;
|
|
|
|
ClassInfo oc = typeid(o);
|
|
int delta = oc.depth;
|
|
|
|
if (delta && c.depth)
|
|
{
|
|
delta -= c.depth;
|
|
if (delta < 0)
|
|
return null;
|
|
|
|
while (delta--)
|
|
oc = oc.base;
|
|
if (areClassInfosEqual(oc, c))
|
|
return cast(void*)o;
|
|
return null;
|
|
}
|
|
|
|
// no depth data - support the old way
|
|
do
|
|
{
|
|
if (areClassInfosEqual(oc, c))
|
|
return cast(void*)o;
|
|
oc = oc.base;
|
|
} while (oc);
|
|
return null;
|
|
}
|
|
|
|
/*****
|
|
* Dynamic cast from a class object o to class type `To`, where `To` is a subclass type of `o`.
|
|
* Params:
|
|
* o = instance of class
|
|
* To = a subclass type of o
|
|
* Returns:
|
|
* null if `o` is null or `To` is not a subclass type of `o`. Otherwise, return `o`.
|
|
*/
|
|
private void* _d_class_cast(To)(const return scope Object o)
|
|
{
|
|
return _d_class_cast_impl(o, typeid(To));
|
|
}
|
|
|
|
/*************************************
|
|
* Attempts to cast interface Object o to class type `To`.
|
|
* Returns o if successful, null if not.
|
|
*/
|
|
private void* _d_interface_cast(To)(void* p) @trusted
|
|
{
|
|
if (!p)
|
|
return null;
|
|
|
|
Interface* pi = **cast(Interface***) p;
|
|
|
|
Object o2 = cast(Object)(p - pi.offset);
|
|
void* res = null;
|
|
size_t offset = 0;
|
|
if (o2 && _d_isbaseof2!To(typeid(o2), offset))
|
|
{
|
|
res = cast(void*) o2 + offset;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* Hook that detects the type of cast performed and calls the appropriate function.
|
|
* Params:
|
|
* o = object that is being casted
|
|
* To = type to which the object is being casted
|
|
* Returns:
|
|
* null if the cast fails, otherwise returns the object casted to the type `To`.
|
|
*/
|
|
void* _d_cast(To, From)(From o) @trusted
|
|
{
|
|
static if (is(From == To))
|
|
{
|
|
return *cast(void**) &o;
|
|
}
|
|
else static if (is(From == class) && is(To == interface))
|
|
{
|
|
return _d_dynamic_cast!To(o);
|
|
}
|
|
else static if (is(From == class) && is(To == class))
|
|
{
|
|
|
|
/* Check for:
|
|
* class A { }
|
|
* final class B : A { }
|
|
* ... cast(B) A ...
|
|
*/
|
|
/* Multiple inheritance is not allowed, so we can safely assume
|
|
* that the second super can only be an interface.
|
|
*/
|
|
static if (is(From FromSupers == super) && is(To ToSupers == super) &&
|
|
__traits(isFinalClass, To) && is(ToSupers[0] == From) &&
|
|
ToSupers.length == 1 && FromSupers.length <= 1)
|
|
{
|
|
return _d_paint_cast!To(o);
|
|
}
|
|
//else static if (is (To : From))
|
|
//{
|
|
// return _d_class_cast!To(o);
|
|
//}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
else static if (is(From == interface))
|
|
{
|
|
return _d_interface_cast!To(cast(void*)o);
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private bool _d_isbaseof2(To)(scope ClassInfo oc, scope ref size_t offset)
|
|
{
|
|
auto c = typeid(To).info;
|
|
|
|
if (areClassInfosEqual(oc, c))
|
|
return true;
|
|
|
|
do
|
|
{
|
|
if (oc.base && areClassInfosEqual(oc.base, c))
|
|
return true;
|
|
|
|
// Bugzilla 2013: Use depth-first search to calculate offset
|
|
// from the derived (oc) to the base (c).
|
|
foreach (iface; oc.interfaces)
|
|
{
|
|
if (areClassInfosEqual(iface.classinfo, c) || _d_isbaseof2!To(iface.classinfo, offset))
|
|
{
|
|
offset += iface.offset;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
oc = oc.base;
|
|
} while (oc);
|
|
|
|
return false;
|
|
}
|