diff --git a/source/lifetime/common.d b/source/lifetime/common.d index 03a0bb7..a7923c4 100644 --- a/source/lifetime/common.d +++ b/source/lifetime/common.d @@ -87,4 +87,35 @@ package void zeroMem(void* ptr, const size_t length) pure nothrow @nogc { foreach(i; 0 .. length) { ptru[i] = 0; } +} + +void emplaceInitializer(T)(scope ref T chunk) nothrow pure @trusted +if (!is(T == const) && !is(T == immutable) && !is(T == inout)) +{ + import core.internal.traits : hasElaborateAssign; + + static if (__traits(isZeroInit, T)) + { + import core.stdc.string : memset; + memset(cast(void*) &chunk, 0, T.sizeof); + } + else static if (__traits(isScalar, T) || + T.sizeof <= 16 && !hasElaborateAssign!T && __traits(compiles, (){ T chunk; chunk = T.init; })) + { + chunk = T.init; + } + else static if (__traits(isStaticArray, T)) + { + // For static arrays there is no initializer symbol created. Instead, we emplace elements one-by-one. + foreach (i; 0 .. T.length) + { + emplaceInitializer(chunk[i]); + } + } + else + { + import core.stdc.string : memcpy; + const initializer = __traits(initSymbol, T); + memcpy(cast(void*)&chunk, initializer.ptr, initializer.length); + } } \ No newline at end of file diff --git a/source/lifetime/destruction.d b/source/lifetime/destruction.d new file mode 100644 index 0000000..1a0fb98 --- /dev/null +++ b/source/lifetime/destruction.d @@ -0,0 +1,29 @@ +module lifetime.destruction; + +//Copy pasted from druntime + +void __ArrayDtor(T)(scope T[] a) +{ + foreach_reverse (ref T e; a) + e.__xdtor(); +} + +public void destructRecurse(E, size_t n)(ref E[n] arr) +{ + import core.internal.traits : hasElaborateDestructor; + + static if (hasElaborateDestructor!E) + { + foreach_reverse (ref elem; arr) + destructRecurse(elem); + } +} + +public void destructRecurse(S)(ref S s) + if (is(S == struct)) +{ + static if (__traits(hasMember, S, "__xdtor") && + // Bugzilla 14746: Check that it's the exact member of S. + __traits(isSame, S, __traits(parent, s.__xdtor))) + s.__xdtor(); +} \ No newline at end of file diff --git a/source/object.d b/source/object.d index abec0e0..9015866 100644 --- a/source/object.d +++ b/source/object.d @@ -12,6 +12,7 @@ public import lifetime.array_ : _d_arraysetlengthTImpl, _d_newarrayU; public import lwdr.string_switch : __switch; public import core.internal.switch_ : __switch_error; //final switch +public import lifetime.destruction: __ArrayDtor; public import rt.arrcast : __ArrayCast; @@ -884,4 +885,67 @@ const: auto p = cast(immutable char*) addrOf(MIname); return p[0 .. strlen(p)]; } +} + +/** + Copy pasted from druntime destroy functions + + +Destroys the given object and optionally resets to initial state. It's used to +_destroy an object, calling its destructor or finalizer so it no longer +references any other objects. It does $(I not) initiate a GC cycle or free +any GC memory. +If `initialize` is supplied `false`, the object is considered invalid after +destruction, and should not be referenced. +*/ +void destroy(bool initialize = true, T)(ref T obj) if (is(T == struct)) +{ + import lifetime.destruction : destructRecurse; + + destructRecurse(obj); + + static if (initialize) + { + import lifetime.common : emplaceInitializer; + emplaceInitializer(obj); // emplace T.init + } +} + + +/// ditto +void destroy(bool initialize = true, T)(T obj) if (is(T == class)) +{ + static if (__traits(getLinkage, T) == "C++") + { + static if (__traits(hasMember, T, "__xdtor")) + obj.__xdtor(); + + static if (initialize) + { + const initializer = __traits(initSymbol, T); + (cast(void*)obj)[0 .. initializer.length] = initializer[]; + } + } + else + { + // Bypass overloaded opCast + auto ptr = (() @trusted => *cast(void**) &obj)(); + rt_finalize2(ptr, true, initialize); + } +} + +/// ditto +void destroy(bool initialize = true, T)(T obj) if (is(T == interface)) +{ + static assert(__traits(getLinkage, T) == "D", "Invalid call to destroy() on extern(" ~ __traits(getLinkage, T) ~ ") interface"); + + destroy!initialize(cast(Object)obj); +} + +/// ditto +void destroy(bool initialize = true, T)(ref T obj) + if (!is(T == struct) && !is(T == interface) && !is(T == class) && !__traits(isStaticArray, T)) +{ + static if (initialize) + obj = T.init; } \ No newline at end of file