| 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; |
| } |
| |
| @safe pure unittest |
| { |
| interface I {} |
| |
| class A {} |
| class B : A {} |
| class C : B, I{} |
| |
| A ac = new C(); |
| assert(_d_cast!I(ac) !is null); // A(c) to I |
| assert(_d_dynamic_cast!I(ac) !is null); |
| |
| A ab = new B(); |
| assert(_d_cast!I(ab) is null); // A(b) to I |
| assert(_d_dynamic_cast!I(ab) is null); |
| } |
| |
| @safe pure unittest |
| { |
| class A {} |
| class B : A {} |
| class C : B {} |
| final class D : C {} |
| |
| C cd = new D(); |
| assert(_d_cast!D(cd) !is null); // C(d) to D |
| assert(_d_paint_cast!D(cd) !is null); |
| |
| class G {} |
| final class J {} |
| A a = new A(); |
| assert(_d_cast!G(a) is null); // A(a) to G |
| assert(_d_paint_cast!G(a) is null); |
| |
| assert(_d_cast!J(a) is null); // A(a) to J |
| assert(_d_paint_cast!J(a) is null); |
| } |
| |
| @safe pure unittest |
| { |
| class A {} |
| class B : A {} |
| class C : B {} |
| class D {} |
| |
| A ac = new C(); |
| assert(_d_cast!C(ac) !is null); // A(c) to C |
| assert(_d_class_cast!C(ac) !is null); |
| |
| assert(_d_cast!B(ac) !is null); // A(c) to B |
| assert(_d_class_cast!B(ac) !is null); |
| |
| A ab = new B(); |
| assert(_d_cast!C(ab) is null); // A(b) to C |
| assert(_d_class_cast!C(ab) is null); |
| |
| A a = new A(); |
| assert(_d_cast!D(a) is null); // A(a) to D |
| assert(_d_class_cast!D(a) is null); |
| } |
| |
| @safe pure unittest |
| { |
| interface I1 {} |
| interface I2 {} |
| interface I3 {} |
| class A {} |
| class B : A, I1, I2 {} |
| class C : B, I3 {} |
| |
| I1 bi = new B(); |
| assert(_d_cast!I2(bi) !is null); // I1(b) to I2 |
| assert(_d_interface_cast!I2(cast(void*)bi) !is null); |
| |
| assert(_d_cast!A(bi) !is null); // I1(b) to A |
| assert(_d_interface_cast!A(cast(void*)bi) !is null); |
| |
| assert(_d_cast!B(bi) !is null); // I1(b) to B |
| assert(_d_interface_cast!B(cast(void*)bi) !is null); |
| |
| assert(_d_cast!I3(bi) is null); // I1(b) to I3 |
| assert(_d_interface_cast!I3(cast(void*)bi) is null); |
| |
| assert(_d_cast!C(bi) is null); // I1(b) to C |
| assert(_d_interface_cast!C(cast(void*)bi) is null); |
| |
| assert(_d_cast!I1(bi) !is null); // I1(b) to I1 |
| assert(_d_interface_cast!I1(cast(void*)bi) !is null); |
| |
| I3 ci = new C(); |
| assert(_d_cast!I1(ci) !is null); // I3(c) to I1 |
| assert(_d_interface_cast!I1(cast(void*)ci) !is null); |
| } |
| |
| // https://github.com/dlang/dmd/issues/21646 |
| @system pure unittest { |
| static class C { |
| @disable void opCast(T)(); |
| } |
| |
| const(C) const_c = new C(); |
| C mutable_c = cast() const_c; |
| } |