diff --git a/src/object.di b/src/object.di index f58c9b3c91..7d9fcf18cc 100644 --- a/src/object.di +++ b/src/object.di @@ -206,7 +206,7 @@ class TypeInfo_Class : TypeInfo void* deallocator; OffsetTypeInfo[] m_offTi; void* defaultConstructor; - immutable(void)* m_rtInfo; // data for precise GC + immutable(void)* m_RTInfo; // data for precise GC static const(TypeInfo_Class) find(in char[] classname); Object create() const; @@ -234,11 +234,16 @@ class TypeInfo_Struct : TypeInfo enum StructFlags : uint { hasPointers = 0x1, + isDynamicType = 0x2, // built at runtime, needs type info in xdtor } StructFlags m_flags; } - void function(void*) xdtor; - void function(void*) xpostblit; + union + { + void function(void*) xdtor; + void function(void*, const TypeInfo_Struct ti) xdtorti; + } + void function(void*) xpostblit; uint m_align; @@ -247,7 +252,7 @@ class TypeInfo_Struct : TypeInfo TypeInfo m_arg1; TypeInfo m_arg2; } - immutable(void)* m_rtInfo; + immutable(void)* m_RTInfo; } class TypeInfo_Tuple : TypeInfo @@ -394,7 +399,7 @@ extern (C) // from druntime/src/rt/aaA.d // size_t _aaLen(in void* p) pure nothrow @nogc; - private void* _aaGetX(void** paa, const TypeInfo keyti, in size_t valuesize, in void* pkey) pure nothrow; + private void* _aaGetY(void** paa, const TypeInfo_AssociativeArray ti, in size_t valuesize, in void* pkey) pure nothrow; // inout(void)* _aaGetRvalueX(inout void* p, in TypeInfo keyti, in size_t valuesize, in void* pkey); inout(void)[] _aaValues(inout void* p, in size_t keysize, in size_t valuesize, const TypeInfo tiValArray) pure nothrow; inout(void)[] _aaKeys(inout void* p, in size_t keysize, const TypeInfo tiKeyArray) pure nothrow; @@ -468,7 +473,7 @@ V[K] dup(T : V[K], K, V)(T aa) { import core.stdc.string : memcpy; - void* pv = _aaGetX(cast(void**)&result, typeid(K), V.sizeof, &k); + void* pv = _aaGetY(cast(void**)&result, typeid(V[K]), V.sizeof, &k); memcpy(pv, &v, V.sizeof); return *cast(V*)pv; } diff --git a/src/object_.d b/src/object_.d index 68b130c77a..0c7d3a1141 100644 --- a/src/object_.d +++ b/src/object_.d @@ -1054,11 +1054,14 @@ class TypeInfo_Struct : TypeInfo override @property size_t talign() nothrow pure const { return m_align; } - override void destroy(void* p) const + final override void destroy(void* p) const { if (xdtor) { - (*xdtor)(p); + if (m_flags & StructFlags.isDynamicType) + (*xdtorti)(p, this); + else + (*xdtor)(p); } } @@ -1081,10 +1084,15 @@ class TypeInfo_Struct : TypeInfo enum StructFlags : uint { hasPointers = 0x1, + isDynamicType = 0x2, // built at runtime, needs type info in xdtor } StructFlags m_flags; } - void function(void*) xdtor; + union + { + void function(void*) xdtor; + void function(void*, const TypeInfo_Struct ti) xdtorti; + } void function(void*) xpostblit; uint m_align; @@ -1983,7 +1991,7 @@ extern (C) // from druntime/src/rt/aaA.d // size_t _aaLen(in void* p) pure nothrow @nogc; - private void* _aaGetX(void** paa, const TypeInfo keyti, in size_t valuesize, in void* pkey) pure nothrow; + private void* _aaGetY(void** paa, const TypeInfo_AssociativeArray ti, in size_t valuesize, in void* pkey) pure nothrow; // inout(void)* _aaGetRvalueX(inout void* p, in TypeInfo keyti, in size_t valuesize, in void* pkey); inout(void)[] _aaValues(inout void* p, in size_t keysize, in size_t valuesize, const TypeInfo tiValArray) pure nothrow; inout(void)[] _aaKeys(inout void* p, in size_t keysize, const TypeInfo tiKeyArray) pure nothrow; @@ -2062,7 +2070,7 @@ V[K] dup(T : V[K], K, V)(T aa) { import core.stdc.string : memcpy; - void* pv = _aaGetX(cast(void**)&result, typeid(K), V.sizeof, &k); + void* pv = _aaGetY(cast(void**)&result, typeid(V[K]), V.sizeof, &k); memcpy(pv, &v, V.sizeof); return *cast(V*)pv; } diff --git a/src/rt/aaA.d b/src/rt/aaA.d index 3c39553921..1a466727ff 100644 --- a/src/rt/aaA.d +++ b/src/rt/aaA.d @@ -19,7 +19,7 @@ private import core.stdc.string; import core.stdc.stdio; import core.memory; - import rt.lifetime : _d_newarrayU; + import rt.lifetime : _d_newarrayU, _d_newitemT, unqualify, __doPostblit; // Convenience function to make sure the NO_INTERIOR gets set on the // bucket array. @@ -64,14 +64,82 @@ struct Entry size_t hash; /* key */ /* value */ + + static void Dtor(void*p, const TypeInfo_Struct sti) + { + // key and value type info stored after the TypeInfo_Struct by tiEntry() + auto sizeti = __traits(classInstanceSize, TypeInfo_Struct); + auto extra = cast(const(TypeInfo)*) (cast(void*)sti + sizeti); + extra[0].destroy(p + Entry.sizeof); + extra[1].destroy(p + Entry.sizeof + aligntsize(extra[0].tsize)); + } +} + +private bool hasDtor(const TypeInfo ti) +{ + if (!ti) + return false; + if (typeid(ti) is typeid(TypeInfo_Struct)) + if ((cast(TypeInfo_Struct)cast(void*)ti).xdtor) + return true; + if (typeid(ti) is typeid(TypeInfo_StaticArray)) + return hasDtor(unqualify(ti.next())); + + return false; +} + +// build type info for Entry with additional key and value fields +TypeInfo_Struct tiEntry(const TypeInfo keyti, const TypeInfo valti) +{ + auto kti = unqualify(keyti); + auto vti = unqualify(valti); + if (!hasDtor(kti) && !hasDtor(vti)) + return null; + + // save kti and vti after type info for struct + auto sizeti = __traits(classInstanceSize, TypeInfo_Struct); + auto sizeall = sizeti + 2 * (void*).sizeof; + void* p = GC.malloc(sizeall); + memcpy(p, typeid(TypeInfo_Struct).init().ptr, sizeti); + + auto ti = cast(TypeInfo_Struct)p; + auto extra = cast(TypeInfo*) (p + sizeti); + extra[0] = cast() kti; + extra[1] = cast() vti; + + static immutable tiName = __MODULE__ ~ ".Entry!(...)"; + ti.name = tiName; + + // we don't expect the Entry objects to be used outside of this module, so we have control + // over the non-usage of the callback methods and other entries and can keep these null + // xtoHash, xopEquals, xopCmp, xtoString and xpostblit + ti.m_RTInfo = null; + auto sizeEntry = Entry.sizeof + aligntsize(kti.tsize()) + valti.tsize(); + ti.m_init = (cast(char*)null)[0..sizeEntry]; // init length, but not ptr + + // xdtor needs to be built from the dtors of key and value for the GC + ti.xdtorti = &Entry.Dtor; + + ti.m_flags = TypeInfo_Struct.StructFlags.hasPointers | TypeInfo_Struct.StructFlags.isDynamicType; + ti.m_align = cast(uint) aligntsize(1); + + return ti; } struct Impl { + this(const TypeInfo_AssociativeArray ti) + { + _keyti = cast() ti.key; + _entryti = cast() tiEntry(_keyti, ti.next); + } + Entry*[] buckets; size_t nodes; // total number of entries size_t firstUsedBucket; // starting index for first used bucket. - TypeInfo _keyti; + TypeInfo _keyti; // these should be const, but missing tail const for classes make this ugly + TypeInfo_Struct _entryti; + Entry*[4] binit; // initial value of buckets[] @property const(TypeInfo) keyti() const @safe pure nothrow @nogc @@ -160,31 +228,13 @@ body * Get pointer to value in associative array indexed by key. * Add entry for key if it is not already there. */ -void* _aaGetX(AA* aa, const TypeInfo keyti, in size_t valuesize, in void* pkey) -in -{ - assert(aa); -} -body -{ - if (aa.impl is null) - { - aa.impl = new Impl(); - aa.impl.buckets = aa.impl.binit[]; - aa.impl.firstUsedBucket = aa.impl.buckets.length; - aa.impl._keyti = cast() keyti; - } - return _aaGetImpl(aa, keyti, valuesize, pkey); -} - void* _aaGetY(AA* aa, const TypeInfo_AssociativeArray ti, in size_t valuesize, in void* pkey) { if (aa.impl is null) { - aa.impl = new Impl(); + aa.impl = new Impl(ti); aa.impl.buckets = aa.impl.binit[]; aa.impl.firstUsedBucket = aa.impl.buckets.length; - aa.impl._keyti = cast() ti.key; } return _aaGetImpl(aa, ti.key, valuesize, pkey); } @@ -202,13 +252,6 @@ body //printf("keyti = %p\n", keyti); //printf("aa = %p\n", aa); - if (aa.impl is null) - { - aa.impl = new Impl(); - aa.impl.buckets = aa.impl.binit[]; - aa.impl.firstUsedBucket = aa.impl.buckets.length; - aa.impl._keyti = cast() keyti; - } //printf("aa = %p\n", aa); //printf("aa.a = %p\n", aa.a); @@ -234,11 +277,15 @@ body // Not found, create new elem //printf("create new one\n"); size_t size = Entry.sizeof + aligntsize(keytitsize) + valuesize; - e = cast(Entry *) GC.malloc(size, 0); // TODO: needs typeid(Entry+) + if (aa.impl._entryti) + e = cast(Entry *) _d_newitemT(aa.impl._entryti); + else + e = cast(Entry *) GC.malloc(size, 0); // TODO: needs typeid(Entry+) e.next = null; e.hash = key_hash; ubyte* ptail = cast(ubyte*)(e + 1); memcpy(ptail, pkey, keytitsize); + __doPostblit(ptail, keytitsize, unqualify(keyti)); memset(ptail + aligntsize(keytitsize), 0, valuesize); // zero value *pe = e; @@ -264,7 +311,8 @@ Lret: void* _aaGetZ(AA* aa, const TypeInfo keyti, in size_t valuesize, in void* pkey, void *function(void[], void[]) @trusted pure aaLiteral) { - return _aaGetX(aa, keyti, valuesize, pkey); + assert(false, "_aaGetZ not implemented"); + //return _aaGetX(aa, keyti, valuesize, pkey); } // bug 13748 @@ -356,7 +404,7 @@ bool _aaDelX(AA aa, in TypeInfo keyti, in void* pkey) if (!(--aa.impl.nodes)) // reset cache, we know there are no nodes in the aa. aa.impl.firstUsedBucket = aa.impl.buckets.length; - // ee could be freed here, but user code may + // ee could be freed here, but user code may // hold pointers to it return true; } @@ -386,12 +434,13 @@ inout(ArrayRet_t) _aaValues(inout AA aa, in size_t keysize, in size_t valuesize, memcpy(a.ptr + resi * valuesize, cast(byte*)e + Entry.sizeof + alignsize, valuesize); - // TODO: no postblit here? resi++; e = e.next; } } assert(resi == a.length); + // cannot postblit, it might not be pure + //__doPostblit(a.ptr, alignsize, unqualify(tiValueArray.next)); } return *cast(inout ArrayRet_t*)(&a); } @@ -450,6 +499,7 @@ body newImpl.nodes = oldImpl.nodes; newImpl._keyti = oldImpl._keyti; + newImpl._entryti = oldImpl._entryti; *paa.impl = newImpl; } @@ -482,12 +532,13 @@ inout(ArrayRet_t) _aaKeys(inout AA aa, in size_t keysize, const TypeInfo tiKeyAr while (e) { memcpy(&res[resi * keysize], cast(byte*)(e + 1), keysize); - // TODO: no postblit here? resi++; e = e.next; } } assert(resi == len); + // cannot postblit, it might not be pure + //__doPostblit(res, len * kisize, unqualify(tiKeyArray.next)); Array a; a.length = len; @@ -629,8 +680,7 @@ Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, vo } else { - result = new Impl(); - result._keyti = cast() keyti; + result = new Impl(ti); size_t i; for (i = 0; i < prime_list.length - 1; i++) @@ -662,7 +712,10 @@ Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, vo { // Not found, create new elem //printf("create new one\n"); - e = cast(Entry *) GC.malloc(Entry.sizeof + keytsize + valuesize); // TODO: needs typeid(Entry+) + if (result._entryti) + e = cast(Entry *) _d_newitemT(result._entryti); + else + e = cast(Entry *) GC.malloc(Entry.sizeof + keytsize + valuesize); // TODO: needs typeid(Entry+) memcpy(e + 1, pkey, keysize); e.next = null; e.hash = key_hash; diff --git a/src/rt/lifetime.d b/src/rt/lifetime.d index c8ef1fa8f9..01a38b585c 100644 --- a/src/rt/lifetime.d +++ b/src/rt/lifetime.d @@ -183,8 +183,7 @@ extern (C) void _d_delstruct(void** p, TypeInfo_Struct inf) { debug(PRINTF) printf("_d_delstruct(%p, %p)\n", *p, cast(void*)inf); - inf.xdtor(*p); - + inf.destroy(*p); GC.free(*p); *p = null; } @@ -1363,7 +1362,8 @@ void finalize_array(void* p, size_t size, const TypeInfo_Struct si) auto tsize = si.tsize; for (auto curP = p + size - tsize; curP >= p; curP -= tsize) { - si.xdtor(curP); // call destructor + // call destructor + si.destroy(curP); } } @@ -1375,8 +1375,7 @@ void finalize_struct(void* p, size_t size) nothrow auto ti = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof); try { - if (ti.xdtor) - ti.xdtor(p); // call destructor + ti.destroy(p); // call destructor } catch (Exception e) { @@ -2494,6 +2493,8 @@ unittest __gshared int dtorCount; static struct S1 { + int x; + ~this() { dtorCount++; @@ -2552,6 +2553,45 @@ unittest assert(dtorCount == 6); GC.free(blkinf.base); } + + // associative arrays + import rt.aaA; + // throw away all existing AA entries with dtor + GC.runFinalizers((cast(char*)(&Entry.Dtor))[0..1]); + + S1[int] aa1; + aa1[0] = S1(0); + aa1[1] = S1(1); + if (callStructDtorsDuringGC) + { + dtorCount = 0; + aa1 = null; + GC.runFinalizers((cast(char*)(&Entry.Dtor))[0..1]); + assert(dtorCount == 2); + } + + int[S1] aa2; + aa2[S1(0)] = 0; + aa2[S1(1)] = 1; + aa2[S1(2)] = 2; + if (callStructDtorsDuringGC) + { + dtorCount = 0; + aa2 = null; + GC.runFinalizers((cast(char*)(&Entry.Dtor))[0..1]); + assert(dtorCount == 3); + } + + S1[2][int] aa3; + aa3[0] = [S1(0),S1(2)]; + aa3[1] = [S1(1),S1(3)]; + if (callStructDtorsDuringGC) + { + dtorCount = 0; + aa3 = null; + GC.runFinalizers((cast(char*)(&Entry.Dtor))[0..1]); + assert(dtorCount == 4); + } } // test class finalizers exception handling