| /** | |
| * Forms the symbols available to all D programs. Includes Object, which is | |
| * the root of the class object hierarchy. This module is implicitly | |
| * imported. | |
| * | |
| * Copyright: Copyright Digital Mars 2000 - 2011. | |
| * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). | |
| * Authors: Walter Bright, Sean Kelly | |
| */ | |
| module object; | |
| private | |
| { | |
| extern (C) Object _d_newclass(const TypeInfo_Class ci); | |
| extern (C) void rt_finalize(void *data, bool det=true); | |
| } | |
| // NOTE: For some reason, this declaration method doesn't work | |
| // in this particular file (and this file only). It must | |
| // be a DMD thing. | |
| //alias typeof(int.sizeof) size_t; | |
| //alias typeof(cast(void*)0 - cast(void*)0) ptrdiff_t; | |
| version (D_LP64) | |
| { | |
| alias size_t = ulong; | |
| alias ptrdiff_t = long; | |
| } | |
| else | |
| { | |
| alias size_t = uint; | |
| alias ptrdiff_t = int; | |
| } | |
| alias sizediff_t = ptrdiff_t; //For backwards compatibility only. | |
| alias hash_t = size_t; //For backwards compatibility only. | |
| alias equals_t = bool; //For backwards compatibility only. | |
| alias string = immutable(char)[]; | |
| alias wstring = immutable(wchar)[]; | |
| alias dstring = immutable(dchar)[]; | |
| version (D_ObjectiveC) public import core.attribute : selector; | |
| /** | |
| * All D class objects inherit from Object. | |
| */ | |
| class Object | |
| { | |
| /** | |
| * Convert Object to a human readable string. | |
| */ | |
| string toString() | |
| { | |
| return typeid(this).name; | |
| } | |
| /** | |
| * Compute hash function for Object. | |
| */ | |
| size_t toHash() @trusted nothrow | |
| { | |
| // BUG: this prevents a compacting GC from working, needs to be fixed | |
| size_t addr = cast(size_t) cast(void*) this; | |
| // The bottom log2((void*).alignof) bits of the address will always | |
| // be 0. Moreover it is likely that each Object is allocated with a | |
| // separate call to malloc. The alignment of malloc differs from | |
| // platform to platform, but rather than having special cases for | |
| // each platform it is safe to use a shift of 4. To minimize | |
| // collisions in the low bits it is more important for the shift to | |
| // not be too small than for the shift to not be too big. | |
| return addr ^ (addr >>> 4); | |
| } | |
| /** | |
| * Compare with another Object obj. | |
| * Returns: | |
| * $(TABLE | |
| * $(TR $(TD this < obj) $(TD < 0)) | |
| * $(TR $(TD this == obj) $(TD 0)) | |
| * $(TR $(TD this > obj) $(TD > 0)) | |
| * ) | |
| */ | |
| int opCmp(Object o) | |
| { | |
| // BUG: this prevents a compacting GC from working, needs to be fixed | |
| //return cast(int)cast(void*)this - cast(int)cast(void*)o; | |
| throw new Exception("need opCmp for class " ~ typeid(this).name); | |
| //return this !is o; | |
| } | |
| /** | |
| * Test whether $(D this) is equal to $(D o). | |
| * The default implementation only compares by identity (using the $(D is) operator). | |
| * Generally, overrides for $(D opEquals) should attempt to compare objects by their contents. | |
| */ | |
| bool opEquals(Object o) | |
| { | |
| return this is o; | |
| } | |
| interface Monitor | |
| { | |
| void lock(); | |
| void unlock(); | |
| } | |
| /** | |
| * Create instance of class specified by the fully qualified name | |
| * classname. | |
| * The class must either have no constructors or have | |
| * a default constructor. | |
| * Returns: | |
| * null if failed | |
| * Example: | |
| * --- | |
| * module foo.bar; | |
| * | |
| * class C | |
| * { | |
| * this() { x = 10; } | |
| * int x; | |
| * } | |
| * | |
| * void main() | |
| * { | |
| * auto c = cast(C)Object.factory("foo.bar.C"); | |
| * assert(c !is null && c.x == 10); | |
| * } | |
| * --- | |
| */ | |
| static Object factory(string classname) | |
| { | |
| auto ci = TypeInfo_Class.find(classname); | |
| if (ci) | |
| { | |
| return ci.create(); | |
| } | |
| return null; | |
| } | |
| } | |
| auto opEquals(Object lhs, Object rhs) | |
| { | |
| // If aliased to the same object or both null => equal | |
| if (lhs is rhs) return true; | |
| // If either is null => non-equal | |
| if (lhs is null || rhs is null) return false; | |
| // If same exact type => one call to method opEquals | |
| if (typeid(lhs) is typeid(rhs) || | |
| !__ctfe && typeid(lhs).opEquals(typeid(rhs))) | |
| /* CTFE doesn't like typeid much. 'is' works, but opEquals doesn't | |
| (issue 7147). But CTFE also guarantees that equal TypeInfos are | |
| always identical. So, no opEquals needed during CTFE. */ | |
| { | |
| return lhs.opEquals(rhs); | |
| } | |
| // General case => symmetric calls to method opEquals | |
| return lhs.opEquals(rhs) && rhs.opEquals(lhs); | |
| } | |
| /************************ | |
| * Returns true if lhs and rhs are equal. | |
| */ | |
| auto opEquals(const Object lhs, const Object rhs) | |
| { | |
| // A hack for the moment. | |
| return opEquals(cast()lhs, cast()rhs); | |
| } | |
| private extern(C) void _d_setSameMutex(shared Object ownee, shared Object owner) nothrow; | |
| void setSameMutex(shared Object ownee, shared Object owner) | |
| { | |
| _d_setSameMutex(ownee, owner); | |
| } | |
| /** | |
| * Information about an interface. | |
| * When an object is accessed via an interface, an Interface* appears as the | |
| * first entry in its vtbl. | |
| */ | |
| struct Interface | |
| { | |
| TypeInfo_Class classinfo; /// .classinfo for this interface (not for containing class) | |
| void*[] vtbl; | |
| size_t offset; /// offset to Interface 'this' from Object 'this' | |
| } | |
| /** | |
| * Array of pairs giving the offset and type information for each | |
| * member in an aggregate. | |
| */ | |
| struct OffsetTypeInfo | |
| { | |
| size_t offset; /// Offset of member from start of object | |
| TypeInfo ti; /// TypeInfo for this member | |
| } | |
| /** | |
| * Runtime type information about a type. | |
| * Can be retrieved for any type using a | |
| * $(GLINK2 expression,TypeidExpression, TypeidExpression). | |
| */ | |
| class TypeInfo | |
| { | |
| override string toString() const pure @safe nothrow | |
| { | |
| return typeid(this).name; | |
| } | |
| override size_t toHash() @trusted const nothrow | |
| { | |
| return hashOf(this.toString()); | |
| } | |
| override int opCmp(Object o) | |
| { | |
| import core.internal.traits : externDFunc; | |
| alias dstrcmp = externDFunc!("core.internal.string.dstrcmp", | |
| int function(scope const char[] s1, scope const char[] s2) @trusted pure nothrow @nogc); | |
| if (this is o) | |
| return 0; | |
| TypeInfo ti = cast(TypeInfo)o; | |
| if (ti is null) | |
| return 1; | |
| return dstrcmp(this.toString(), ti.toString()); | |
| } | |
| override bool opEquals(Object o) | |
| { | |
| /* TypeInfo instances are singletons, but duplicates can exist | |
| * across DLL's. Therefore, comparing for a name match is | |
| * sufficient. | |
| */ | |
| if (this is o) | |
| return true; | |
| auto ti = cast(const TypeInfo)o; | |
| return ti && this.toString() == ti.toString(); | |
| } | |
| /** | |
| * Computes a hash of the instance of a type. | |
| * Params: | |
| * p = pointer to start of instance of the type | |
| * Returns: | |
| * the hash | |
| * Bugs: | |
| * fix https://issues.dlang.org/show_bug.cgi?id=12516 e.g. by changing this to a truly safe interface. | |
| */ | |
| size_t getHash(scope const void* p) @trusted nothrow const | |
| { | |
| return hashOf(p); | |
| } | |
| /// Compares two instances for equality. | |
| bool equals(in void* p1, in void* p2) const { return p1 == p2; } | |
| /// Compares two instances for <, ==, or >. | |
| int compare(in void* p1, in void* p2) const { return _xopCmp(p1, p2); } | |
| /// Returns size of the type. | |
| @property size_t tsize() nothrow pure const @safe @nogc { return 0; } | |
| /// Swaps two instances of the type. | |
| void swap(void* p1, void* p2) const | |
| { | |
| immutable size_t n = tsize; | |
| for (size_t i = 0; i < n; i++) | |
| { | |
| byte t = (cast(byte *)p1)[i]; | |
| (cast(byte*)p1)[i] = (cast(byte*)p2)[i]; | |
| (cast(byte*)p2)[i] = t; | |
| } | |
| } | |
| /** Get TypeInfo for 'next' type, as defined by what kind of type this is, | |
| null if none. */ | |
| @property inout(TypeInfo) next() nothrow pure inout @nogc { return null; } | |
| /** | |
| * Return default initializer. If the type should be initialized to all | |
| * zeros, an array with a null ptr and a length equal to the type size will | |
| * be returned. For static arrays, this returns the default initializer for | |
| * a single element of the array, use `tsize` to get the correct size. | |
| */ | |
| abstract const(void)[] initializer() nothrow pure const @safe @nogc; | |
| /** Get flags for type: 1 means GC should scan for pointers, | |
| 2 means arg of this type is passed in XMM register */ | |
| @property uint flags() nothrow pure const @safe @nogc { return 0; } | |
| /// Get type information on the contents of the type; null if not available | |
| const(OffsetTypeInfo)[] offTi() const { return null; } | |
| /// Run the destructor on the object and all its sub-objects | |
| void destroy(void* p) const {} | |
| /// Run the postblit on the object and all its sub-objects | |
| void postblit(void* p) const {} | |
| /// Return alignment of type | |
| @property size_t talign() nothrow pure const @safe @nogc { return tsize; } | |
| /** Return internal info on arguments fitting into 8byte. | |
| * See X86-64 ABI 3.2.3 | |
| */ | |
| version (X86_64) int argTypes(out TypeInfo arg1, out TypeInfo arg2) @safe nothrow | |
| { | |
| arg1 = this; | |
| return 0; | |
| } | |
| /** Return info used by the garbage collector to do precise collection. | |
| */ | |
| @property immutable(void)* rtInfo() nothrow pure const @safe @nogc { return null; } | |
| } | |
| class TypeInfo_Enum : TypeInfo | |
| { | |
| override string toString() const { return name; } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_Enum)o; | |
| return c && this.name == c.name && | |
| this.base == c.base; | |
| } | |
| override size_t getHash(scope const void* p) const { return base.getHash(p); } | |
| override bool equals(in void* p1, in void* p2) const { return base.equals(p1, p2); } | |
| override int compare(in void* p1, in void* p2) const { return base.compare(p1, p2); } | |
| override @property size_t tsize() nothrow pure const { return base.tsize; } | |
| override void swap(void* p1, void* p2) const { return base.swap(p1, p2); } | |
| override @property inout(TypeInfo) next() nothrow pure inout { return base.next; } | |
| override @property uint flags() nothrow pure const { return base.flags; } | |
| override const(void)[] initializer() const | |
| { | |
| return m_init.length ? m_init : base.initializer(); | |
| } | |
| override @property size_t talign() nothrow pure const { return base.talign; } | |
| version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| return base.argTypes(arg1, arg2); | |
| } | |
| override @property immutable(void)* rtInfo() const { return base.rtInfo; } | |
| TypeInfo base; | |
| string name; | |
| void[] m_init; | |
| } | |
| unittest // issue 12233 | |
| { | |
| static assert(is(typeof(TypeInfo.init) == TypeInfo)); | |
| assert(TypeInfo.init is null); | |
| } | |
| // Please make sure to keep this in sync with TypeInfo_P (src/rt/typeinfo/ti_ptr.d) | |
| class TypeInfo_Pointer : TypeInfo | |
| { | |
| override string toString() const { return m_next.toString() ~ "*"; } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_Pointer)o; | |
| return c && this.m_next == c.m_next; | |
| } | |
| override size_t getHash(scope const void* p) @trusted const | |
| { | |
| size_t addr = cast(size_t) *cast(const void**)p; | |
| return addr ^ (addr >> 4); | |
| } | |
| override bool equals(in void* p1, in void* p2) const | |
| { | |
| return *cast(void**)p1 == *cast(void**)p2; | |
| } | |
| override int compare(in void* p1, in void* p2) const | |
| { | |
| if (*cast(void**)p1 < *cast(void**)p2) | |
| return -1; | |
| else if (*cast(void**)p1 > *cast(void**)p2) | |
| return 1; | |
| else | |
| return 0; | |
| } | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| return (void*).sizeof; | |
| } | |
| override const(void)[] initializer() const @trusted | |
| { | |
| return (cast(void *)null)[0 .. (void*).sizeof]; | |
| } | |
| override void swap(void* p1, void* p2) const | |
| { | |
| void* tmp = *cast(void**)p1; | |
| *cast(void**)p1 = *cast(void**)p2; | |
| *cast(void**)p2 = tmp; | |
| } | |
| override @property inout(TypeInfo) next() nothrow pure inout { return m_next; } | |
| override @property uint flags() nothrow pure const { return 1; } | |
| TypeInfo m_next; | |
| } | |
| class TypeInfo_Array : TypeInfo | |
| { | |
| override string toString() const { return value.toString() ~ "[]"; } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_Array)o; | |
| return c && this.value == c.value; | |
| } | |
| override size_t getHash(scope const void* p) @trusted const | |
| { | |
| void[] a = *cast(void[]*)p; | |
| return getArrayHash(value, a.ptr, a.length); | |
| } | |
| override bool equals(in void* p1, in void* p2) const | |
| { | |
| void[] a1 = *cast(void[]*)p1; | |
| void[] a2 = *cast(void[]*)p2; | |
| if (a1.length != a2.length) | |
| return false; | |
| size_t sz = value.tsize; | |
| for (size_t i = 0; i < a1.length; i++) | |
| { | |
| if (!value.equals(a1.ptr + i * sz, a2.ptr + i * sz)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| override int compare(in void* p1, in void* p2) const | |
| { | |
| void[] a1 = *cast(void[]*)p1; | |
| void[] a2 = *cast(void[]*)p2; | |
| size_t sz = value.tsize; | |
| size_t len = a1.length; | |
| if (a2.length < len) | |
| len = a2.length; | |
| for (size_t u = 0; u < len; u++) | |
| { | |
| immutable int result = value.compare(a1.ptr + u * sz, a2.ptr + u * sz); | |
| if (result) | |
| return result; | |
| } | |
| return cast(int)a1.length - cast(int)a2.length; | |
| } | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| return (void[]).sizeof; | |
| } | |
| override const(void)[] initializer() const @trusted | |
| { | |
| return (cast(void *)null)[0 .. (void[]).sizeof]; | |
| } | |
| override void swap(void* p1, void* p2) const | |
| { | |
| void[] tmp = *cast(void[]*)p1; | |
| *cast(void[]*)p1 = *cast(void[]*)p2; | |
| *cast(void[]*)p2 = tmp; | |
| } | |
| TypeInfo value; | |
| override @property inout(TypeInfo) next() nothrow pure inout | |
| { | |
| return value; | |
| } | |
| override @property uint flags() nothrow pure const { return 1; } | |
| override @property size_t talign() nothrow pure const | |
| { | |
| return (void[]).alignof; | |
| } | |
| version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| arg1 = typeid(size_t); | |
| arg2 = typeid(void*); | |
| return 0; | |
| } | |
| } | |
| class TypeInfo_StaticArray : TypeInfo | |
| { | |
| override string toString() const | |
| { | |
| import core.internal.traits : externDFunc; | |
| alias sizeToTempString = externDFunc!("core.internal.string.unsignedToTempString", | |
| char[] function(ulong, return char[], uint) @safe pure nothrow @nogc); | |
| char[20] tmpBuff = void; | |
| return value.toString() ~ "[" ~ sizeToTempString(len, tmpBuff, 10) ~ "]"; | |
| } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_StaticArray)o; | |
| return c && this.len == c.len && | |
| this.value == c.value; | |
| } | |
| override size_t getHash(scope const void* p) @trusted const | |
| { | |
| return getArrayHash(value, p, len); | |
| } | |
| override bool equals(in void* p1, in void* p2) const | |
| { | |
| size_t sz = value.tsize; | |
| for (size_t u = 0; u < len; u++) | |
| { | |
| if (!value.equals(p1 + u * sz, p2 + u * sz)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| override int compare(in void* p1, in void* p2) const | |
| { | |
| size_t sz = value.tsize; | |
| for (size_t u = 0; u < len; u++) | |
| { | |
| immutable int result = value.compare(p1 + u * sz, p2 + u * sz); | |
| if (result) | |
| return result; | |
| } | |
| return 0; | |
| } | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| return len * value.tsize; | |
| } | |
| override void swap(void* p1, void* p2) const | |
| { | |
| import core.memory; | |
| import core.stdc.string : memcpy; | |
| void* tmp; | |
| size_t sz = value.tsize; | |
| ubyte[16] buffer; | |
| void* pbuffer; | |
| if (sz < buffer.sizeof) | |
| tmp = buffer.ptr; | |
| else | |
| tmp = pbuffer = (new void[sz]).ptr; | |
| for (size_t u = 0; u < len; u += sz) | |
| { | |
| size_t o = u * sz; | |
| memcpy(tmp, p1 + o, sz); | |
| memcpy(p1 + o, p2 + o, sz); | |
| memcpy(p2 + o, tmp, sz); | |
| } | |
| if (pbuffer) | |
| GC.free(pbuffer); | |
| } | |
| override const(void)[] initializer() nothrow pure const | |
| { | |
| return value.initializer(); | |
| } | |
| override @property inout(TypeInfo) next() nothrow pure inout { return value; } | |
| override @property uint flags() nothrow pure const { return value.flags; } | |
| override void destroy(void* p) const | |
| { | |
| immutable sz = value.tsize; | |
| p += sz * len; | |
| foreach (i; 0 .. len) | |
| { | |
| p -= sz; | |
| value.destroy(p); | |
| } | |
| } | |
| override void postblit(void* p) const | |
| { | |
| immutable sz = value.tsize; | |
| foreach (i; 0 .. len) | |
| { | |
| value.postblit(p); | |
| p += sz; | |
| } | |
| } | |
| TypeInfo value; | |
| size_t len; | |
| override @property size_t talign() nothrow pure const | |
| { | |
| return value.talign; | |
| } | |
| version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| arg1 = typeid(void*); | |
| return 0; | |
| } | |
| } | |
| class TypeInfo_AssociativeArray : TypeInfo | |
| { | |
| override string toString() const | |
| { | |
| return value.toString() ~ "[" ~ key.toString() ~ "]"; | |
| } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_AssociativeArray)o; | |
| return c && this.key == c.key && | |
| this.value == c.value; | |
| } | |
| override bool equals(in void* p1, in void* p2) @trusted const | |
| { | |
| return !!_aaEqual(this, *cast(const AA*) p1, *cast(const AA*) p2); | |
| } | |
| override hash_t getHash(scope const void* p) nothrow @trusted const | |
| { | |
| return _aaGetHash(cast(AA*)p, this); | |
| } | |
| // BUG: need to add the rest of the functions | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| return (char[int]).sizeof; | |
| } | |
| override const(void)[] initializer() const @trusted | |
| { | |
| return (cast(void *)null)[0 .. (char[int]).sizeof]; | |
| } | |
| override @property inout(TypeInfo) next() nothrow pure inout { return value; } | |
| override @property uint flags() nothrow pure const { return 1; } | |
| TypeInfo value; | |
| TypeInfo key; | |
| override @property size_t talign() nothrow pure const | |
| { | |
| return (char[int]).alignof; | |
| } | |
| version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| arg1 = typeid(void*); | |
| return 0; | |
| } | |
| } | |
| class TypeInfo_Vector : TypeInfo | |
| { | |
| override string toString() const { return "__vector(" ~ base.toString() ~ ")"; } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_Vector)o; | |
| return c && this.base == c.base; | |
| } | |
| override size_t getHash(scope const void* p) const { return base.getHash(p); } | |
| override bool equals(in void* p1, in void* p2) const { return base.equals(p1, p2); } | |
| override int compare(in void* p1, in void* p2) const { return base.compare(p1, p2); } | |
| override @property size_t tsize() nothrow pure const { return base.tsize; } | |
| override void swap(void* p1, void* p2) const { return base.swap(p1, p2); } | |
| override @property inout(TypeInfo) next() nothrow pure inout { return base.next; } | |
| override @property uint flags() nothrow pure const { return base.flags; } | |
| override const(void)[] initializer() nothrow pure const | |
| { | |
| return base.initializer(); | |
| } | |
| override @property size_t talign() nothrow pure const { return 16; } | |
| version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| return base.argTypes(arg1, arg2); | |
| } | |
| TypeInfo base; | |
| } | |
| class TypeInfo_Function : TypeInfo | |
| { | |
| override string toString() const | |
| { | |
| import core.demangle : demangleType; | |
| alias SafeDemangleFunctionType = char[] function (const(char)[] buf, char[] dst = null) @safe nothrow pure; | |
| SafeDemangleFunctionType demangle = ( () @trusted => cast(SafeDemangleFunctionType)(&demangleType) ) (); | |
| return (() @trusted => cast(string)(demangle(deco))) (); | |
| } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_Function)o; | |
| return c && this.deco == c.deco; | |
| } | |
| // BUG: need to add the rest of the functions | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| return 0; // no size for functions | |
| } | |
| override const(void)[] initializer() const @safe | |
| { | |
| return null; | |
| } | |
| TypeInfo next; | |
| /** | |
| * Mangled function type string | |
| */ | |
| string deco; | |
| } | |
| unittest | |
| { | |
| abstract class C | |
| { | |
| void func(); | |
| void func(int a); | |
| int func(int a, int b); | |
| } | |
| alias functionTypes = typeof(__traits(getVirtualFunctions, C, "func")); | |
| assert(typeid(functionTypes[0]).toString() == "void function()"); | |
| assert(typeid(functionTypes[1]).toString() == "void function(int)"); | |
| assert(typeid(functionTypes[2]).toString() == "int function(int, int)"); | |
| } | |
| class TypeInfo_Delegate : TypeInfo | |
| { | |
| override string toString() const | |
| { | |
| return cast(string)(next.toString() ~ " delegate()"); | |
| } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_Delegate)o; | |
| return c && this.deco == c.deco; | |
| } | |
| override size_t getHash(scope const void* p) @trusted const | |
| { | |
| return hashOf(*cast(void delegate()*)p); | |
| } | |
| override bool equals(in void* p1, in void* p2) const | |
| { | |
| auto dg1 = *cast(void delegate()*)p1; | |
| auto dg2 = *cast(void delegate()*)p2; | |
| return dg1 == dg2; | |
| } | |
| override int compare(in void* p1, in void* p2) const | |
| { | |
| auto dg1 = *cast(void delegate()*)p1; | |
| auto dg2 = *cast(void delegate()*)p2; | |
| if (dg1 < dg2) | |
| return -1; | |
| else if (dg1 > dg2) | |
| return 1; | |
| else | |
| return 0; | |
| } | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| alias dg = int delegate(); | |
| return dg.sizeof; | |
| } | |
| override const(void)[] initializer() const @trusted | |
| { | |
| return (cast(void *)null)[0 .. (int delegate()).sizeof]; | |
| } | |
| override @property uint flags() nothrow pure const { return 1; } | |
| TypeInfo next; | |
| string deco; | |
| override @property size_t talign() nothrow pure const | |
| { | |
| alias dg = int delegate(); | |
| return dg.alignof; | |
| } | |
| version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| arg1 = typeid(void*); | |
| arg2 = typeid(void*); | |
| return 0; | |
| } | |
| } | |
| /** | |
| * Runtime type information about a class. | |
| * Can be retrieved from an object instance by using the | |
| * $(DDSUBLINK spec/property,classinfo, .classinfo) property. | |
| */ | |
| class TypeInfo_Class : TypeInfo | |
| { | |
| override string toString() const { return info.name; } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_Class)o; | |
| return c && this.info.name == c.info.name; | |
| } | |
| override size_t getHash(scope const void* p) @trusted const | |
| { | |
| auto o = *cast(Object*)p; | |
| return o ? o.toHash() : 0; | |
| } | |
| override bool equals(in void* p1, in void* p2) const | |
| { | |
| Object o1 = *cast(Object*)p1; | |
| Object o2 = *cast(Object*)p2; | |
| return (o1 is o2) || (o1 && o1.opEquals(o2)); | |
| } | |
| override int compare(in void* p1, in void* p2) const | |
| { | |
| Object o1 = *cast(Object*)p1; | |
| Object o2 = *cast(Object*)p2; | |
| int c = 0; | |
| // Regard null references as always being "less than" | |
| if (o1 !is o2) | |
| { | |
| if (o1) | |
| { | |
| if (!o2) | |
| c = 1; | |
| else | |
| c = o1.opCmp(o2); | |
| } | |
| else | |
| c = -1; | |
| } | |
| return c; | |
| } | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| return Object.sizeof; | |
| } | |
| override const(void)[] initializer() nothrow pure const @safe | |
| { | |
| return m_init; | |
| } | |
| override @property uint flags() nothrow pure const { return 1; } | |
| override @property const(OffsetTypeInfo)[] offTi() nothrow pure const | |
| { | |
| return m_offTi; | |
| } | |
| @property auto info() @safe nothrow pure const { return this; } | |
| @property auto typeinfo() @safe nothrow pure const { return this; } | |
| byte[] m_init; /** class static initializer | |
| * (init.length gives size in bytes of class) | |
| */ | |
| string name; /// class name | |
| void*[] vtbl; /// virtual function pointer table | |
| Interface[] interfaces; /// interfaces this class implements | |
| TypeInfo_Class base; /// base class | |
| void* destructor; | |
| void function(Object) classInvariant; | |
| enum ClassFlags : uint | |
| { | |
| isCOMclass = 0x1, | |
| noPointers = 0x2, | |
| hasOffTi = 0x4, | |
| hasCtor = 0x8, | |
| hasGetMembers = 0x10, | |
| hasTypeInfo = 0x20, | |
| isAbstract = 0x40, | |
| isCPPclass = 0x80, | |
| hasDtor = 0x100, | |
| } | |
| ClassFlags m_flags; | |
| void* deallocator; | |
| OffsetTypeInfo[] m_offTi; | |
| void function(Object) defaultConstructor; // default Constructor | |
| immutable(void)* m_RTInfo; // data for precise GC | |
| override @property immutable(void)* rtInfo() const { return m_RTInfo; } | |
| /** | |
| * Search all modules for TypeInfo_Class corresponding to classname. | |
| * Returns: null if not found | |
| */ | |
| static const(TypeInfo_Class) find(in char[] classname) | |
| { | |
| foreach (m; ModuleInfo) | |
| { | |
| if (m) | |
| { | |
| //writefln("module %s, %d", m.name, m.localClasses.length); | |
| foreach (c; m.localClasses) | |
| { | |
| if (c is null) | |
| continue; | |
| //writefln("\tclass %s", c.name); | |
| if (c.name == classname) | |
| return c; | |
| } | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * Create instance of Object represented by 'this'. | |
| */ | |
| Object create() const | |
| { | |
| if (m_flags & 8 && !defaultConstructor) | |
| return null; | |
| if (m_flags & 64) // abstract | |
| return null; | |
| Object o = _d_newclass(this); | |
| if (m_flags & 8 && defaultConstructor) | |
| { | |
| defaultConstructor(o); | |
| } | |
| return o; | |
| } | |
| } | |
| alias ClassInfo = TypeInfo_Class; | |
| unittest | |
| { | |
| // Bugzilla 14401 | |
| static class X | |
| { | |
| int a; | |
| } | |
| assert(typeid(X).initializer is typeid(X).m_init); | |
| assert(typeid(X).initializer.length == typeid(const(X)).initializer.length); | |
| assert(typeid(X).initializer.length == typeid(shared(X)).initializer.length); | |
| assert(typeid(X).initializer.length == typeid(immutable(X)).initializer.length); | |
| } | |
| class TypeInfo_Interface : TypeInfo | |
| { | |
| override string toString() const { return info.name; } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto c = cast(const TypeInfo_Interface)o; | |
| return c && this.info.name == typeid(c).name; | |
| } | |
| override size_t getHash(scope const void* p) @trusted const | |
| { | |
| if (!*cast(void**)p) | |
| { | |
| return 0; | |
| } | |
| Interface* pi = **cast(Interface ***)*cast(void**)p; | |
| Object o = cast(Object)(*cast(void**)p - pi.offset); | |
| assert(o); | |
| return o.toHash(); | |
| } | |
| override bool equals(in void* p1, in void* p2) const | |
| { | |
| Interface* pi = **cast(Interface ***)*cast(void**)p1; | |
| Object o1 = cast(Object)(*cast(void**)p1 - pi.offset); | |
| pi = **cast(Interface ***)*cast(void**)p2; | |
| Object o2 = cast(Object)(*cast(void**)p2 - pi.offset); | |
| return o1 == o2 || (o1 && o1.opCmp(o2) == 0); | |
| } | |
| override int compare(in void* p1, in void* p2) const | |
| { | |
| Interface* pi = **cast(Interface ***)*cast(void**)p1; | |
| Object o1 = cast(Object)(*cast(void**)p1 - pi.offset); | |
| pi = **cast(Interface ***)*cast(void**)p2; | |
| Object o2 = cast(Object)(*cast(void**)p2 - pi.offset); | |
| int c = 0; | |
| // Regard null references as always being "less than" | |
| if (o1 != o2) | |
| { | |
| if (o1) | |
| { | |
| if (!o2) | |
| c = 1; | |
| else | |
| c = o1.opCmp(o2); | |
| } | |
| else | |
| c = -1; | |
| } | |
| return c; | |
| } | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| return Object.sizeof; | |
| } | |
| override const(void)[] initializer() const @trusted | |
| { | |
| return (cast(void *)null)[0 .. Object.sizeof]; | |
| } | |
| override @property uint flags() nothrow pure const { return 1; } | |
| TypeInfo_Class info; | |
| } | |
| class TypeInfo_Struct : TypeInfo | |
| { | |
| override string toString() const { return name; } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto s = cast(const TypeInfo_Struct)o; | |
| return s && this.name == s.name && | |
| this.initializer().length == s.initializer().length; | |
| } | |
| override size_t getHash(scope const void* p) @trusted pure nothrow const | |
| { | |
| assert(p); | |
| if (xtoHash) | |
| { | |
| return (*xtoHash)(p); | |
| } | |
| else | |
| { | |
| return hashOf(p[0 .. initializer().length]); | |
| } | |
| } | |
| override bool equals(in void* p1, in void* p2) @trusted pure nothrow const | |
| { | |
| import core.stdc.string : memcmp; | |
| if (!p1 || !p2) | |
| return false; | |
| else if (xopEquals) | |
| { | |
| version (GNU) | |
| { // BUG: GDC and DMD use different calling conventions | |
| return (*xopEquals)(p2, p1); | |
| } | |
| else | |
| return (*xopEquals)(p1, p2); | |
| } | |
| else if (p1 == p2) | |
| return true; | |
| else | |
| // BUG: relies on the GC not moving objects | |
| return memcmp(p1, p2, initializer().length) == 0; | |
| } | |
| override int compare(in void* p1, in void* p2) @trusted pure nothrow const | |
| { | |
| import core.stdc.string : memcmp; | |
| // Regard null references as always being "less than" | |
| if (p1 != p2) | |
| { | |
| if (p1) | |
| { | |
| if (!p2) | |
| return true; | |
| else if (xopCmp) | |
| { | |
| version (GNU) | |
| { // BUG: GDC and DMD use different calling conventions | |
| return (*xopCmp)(p1, p2); | |
| } | |
| else | |
| return (*xopCmp)(p2, p1); | |
| } | |
| else | |
| // BUG: relies on the GC not moving objects | |
| return memcmp(p1, p2, initializer().length); | |
| } | |
| else | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| return initializer().length; | |
| } | |
| override const(void)[] initializer() nothrow pure const @safe | |
| { | |
| return m_init; | |
| } | |
| override @property uint flags() nothrow pure const { return m_flags; } | |
| override @property size_t talign() nothrow pure const { return m_align; } | |
| final override void destroy(void* p) const | |
| { | |
| if (xdtor) | |
| { | |
| if (m_flags & StructFlags.isDynamicType) | |
| (*xdtorti)(p, this); | |
| else | |
| (*xdtor)(p); | |
| } | |
| } | |
| override void postblit(void* p) const | |
| { | |
| if (xpostblit) | |
| (*xpostblit)(p); | |
| } | |
| string name; | |
| void[] m_init; // initializer; m_init.ptr == null if 0 initialize | |
| @safe pure nothrow | |
| { | |
| size_t function(in void*) xtoHash; | |
| bool function(in void*, in void*) xopEquals; | |
| int function(in void*, in void*) xopCmp; | |
| string function(in void*) xtoString; | |
| enum StructFlags : uint | |
| { | |
| hasPointers = 0x1, | |
| isDynamicType = 0x2, // built at runtime, needs type info in xdtor | |
| } | |
| StructFlags m_flags; | |
| } | |
| union | |
| { | |
| void function(void*) xdtor; | |
| void function(void*, const TypeInfo_Struct ti) xdtorti; | |
| } | |
| void function(void*) xpostblit; | |
| uint m_align; | |
| override @property immutable(void)* rtInfo() const { return m_RTInfo; } | |
| version (X86_64) | |
| { | |
| override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| arg1 = m_arg1; | |
| arg2 = m_arg2; | |
| return 0; | |
| } | |
| TypeInfo m_arg1; | |
| TypeInfo m_arg2; | |
| } | |
| immutable(void)* m_RTInfo; // data for precise GC | |
| } | |
| unittest | |
| { | |
| struct S | |
| { | |
| bool opEquals(ref const S rhs) const | |
| { | |
| return false; | |
| } | |
| } | |
| S s; | |
| assert(!typeid(S).equals(&s, &s)); | |
| } | |
| class TypeInfo_Tuple : TypeInfo | |
| { | |
| TypeInfo[] elements; | |
| override string toString() const | |
| { | |
| string s = "("; | |
| foreach (i, element; elements) | |
| { | |
| if (i) | |
| s ~= ','; | |
| s ~= element.toString(); | |
| } | |
| s ~= ")"; | |
| return s; | |
| } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| auto t = cast(const TypeInfo_Tuple)o; | |
| if (t && elements.length == t.elements.length) | |
| { | |
| for (size_t i = 0; i < elements.length; i++) | |
| { | |
| if (elements[i] != t.elements[i]) | |
| return false; | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| override size_t getHash(scope const void* p) const | |
| { | |
| assert(0); | |
| } | |
| override bool equals(in void* p1, in void* p2) const | |
| { | |
| assert(0); | |
| } | |
| override int compare(in void* p1, in void* p2) const | |
| { | |
| assert(0); | |
| } | |
| override @property size_t tsize() nothrow pure const | |
| { | |
| assert(0); | |
| } | |
| override const(void)[] initializer() const @trusted | |
| { | |
| assert(0); | |
| } | |
| override void swap(void* p1, void* p2) const | |
| { | |
| assert(0); | |
| } | |
| override void destroy(void* p) const | |
| { | |
| assert(0); | |
| } | |
| override void postblit(void* p) const | |
| { | |
| assert(0); | |
| } | |
| override @property size_t talign() nothrow pure const | |
| { | |
| assert(0); | |
| } | |
| version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| assert(0); | |
| } | |
| } | |
| class TypeInfo_Const : TypeInfo | |
| { | |
| override string toString() const | |
| { | |
| return cast(string) ("const(" ~ base.toString() ~ ")"); | |
| } | |
| //override bool opEquals(Object o) { return base.opEquals(o); } | |
| override bool opEquals(Object o) | |
| { | |
| if (this is o) | |
| return true; | |
| if (typeid(this) != typeid(o)) | |
| return false; | |
| auto t = cast(TypeInfo_Const)o; | |
| return base.opEquals(t.base); | |
| } | |
| override size_t getHash(scope const void *p) const { return base.getHash(p); } | |
| override bool equals(in void *p1, in void *p2) const { return base.equals(p1, p2); } | |
| override int compare(in void *p1, in void *p2) const { return base.compare(p1, p2); } | |
| override @property size_t tsize() nothrow pure const { return base.tsize; } | |
| override void swap(void *p1, void *p2) const { return base.swap(p1, p2); } | |
| override @property inout(TypeInfo) next() nothrow pure inout { return base.next; } | |
| override @property uint flags() nothrow pure const { return base.flags; } | |
| override const(void)[] initializer() nothrow pure const | |
| { | |
| return base.initializer(); | |
| } | |
| override @property size_t talign() nothrow pure const { return base.talign; } | |
| version (X86_64) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) | |
| { | |
| return base.argTypes(arg1, arg2); | |
| } | |
| TypeInfo base; | |
| } | |
| class TypeInfo_Invariant : TypeInfo_Const | |
| { | |
| override string toString() const | |
| { | |
| return cast(string) ("immutable(" ~ base.toString() ~ ")"); | |
| } | |
| } | |
| class TypeInfo_Shared : TypeInfo_Const | |
| { | |
| override string toString() const | |
| { | |
| return cast(string) ("shared(" ~ base.toString() ~ ")"); | |
| } | |
| } | |
| class TypeInfo_Inout : TypeInfo_Const | |
| { | |
| override string toString() const | |
| { | |
| return cast(string) ("inout(" ~ base.toString() ~ ")"); | |
| } | |
| } | |
| /////////////////////////////////////////////////////////////////////////////// | |
| // ModuleInfo | |
| /////////////////////////////////////////////////////////////////////////////// | |
| enum | |
| { | |
| MIctorstart = 0x1, // we've started constructing it | |
| MIctordone = 0x2, // finished construction | |
| MIstandalone = 0x4, // module ctor does not depend on other module | |
| // ctors being done first | |
| MItlsctor = 8, | |
| MItlsdtor = 0x10, | |
| MIctor = 0x20, | |
| MIdtor = 0x40, | |
| MIxgetMembers = 0x80, | |
| MIictor = 0x100, | |
| MIunitTest = 0x200, | |
| MIimportedModules = 0x400, | |
| MIlocalClasses = 0x800, | |
| MIname = 0x1000, | |
| } | |
| struct ModuleInfo | |
| { | |
| uint _flags; | |
| uint _index; // index into _moduleinfo_array[] | |
| version (all) | |
| { | |
| deprecated("ModuleInfo cannot be copy-assigned because it is a variable-sized struct.") | |
| void opAssign(in ModuleInfo m) { _flags = m._flags; _index = m._index; } | |
| } | |
| else | |
| { | |
| @disable this(); | |
| @disable this(this) const; | |
| } | |
| const: | |
| private void* addrOf(int flag) nothrow pure @nogc | |
| in | |
| { | |
| assert(flag >= MItlsctor && flag <= MIname); | |
| assert(!(flag & (flag - 1)) && !(flag & ~(flag - 1) << 1)); | |
| } | |
| body | |
| { | |
| import core.stdc.string : strlen; | |
| void* p = cast(void*)&this + ModuleInfo.sizeof; | |
| if (flags & MItlsctor) | |
| { | |
| if (flag == MItlsctor) return p; | |
| p += typeof(tlsctor).sizeof; | |
| } | |
| if (flags & MItlsdtor) | |
| { | |
| if (flag == MItlsdtor) return p; | |
| p += typeof(tlsdtor).sizeof; | |
| } | |
| if (flags & MIctor) | |
| { | |
| if (flag == MIctor) return p; | |
| p += typeof(ctor).sizeof; | |
| } | |
| if (flags & MIdtor) | |
| { | |
| if (flag == MIdtor) return p; | |
| p += typeof(dtor).sizeof; | |
| } | |
| if (flags & MIxgetMembers) | |
| { | |
| if (flag == MIxgetMembers) return p; | |
| p += typeof(xgetMembers).sizeof; | |
| } | |
| if (flags & MIictor) | |
| { | |
| if (flag == MIictor) return p; | |
| p += typeof(ictor).sizeof; | |
| } | |
| if (flags & MIunitTest) | |
| { | |
| if (flag == MIunitTest) return p; | |
| p += typeof(unitTest).sizeof; | |
| } | |
| if (flags & MIimportedModules) | |
| { | |
| if (flag == MIimportedModules) return p; | |
| p += size_t.sizeof + *cast(size_t*)p * typeof(importedModules[0]).sizeof; | |
| } | |
| if (flags & MIlocalClasses) | |
| { | |
| if (flag == MIlocalClasses) return p; | |
| p += size_t.sizeof + *cast(size_t*)p * typeof(localClasses[0]).sizeof; | |
| } | |
| if (true || flags & MIname) // always available for now | |
| { | |
| if (flag == MIname) return p; | |
| p += strlen(cast(immutable char*)p); | |
| } | |
| assert(0); | |
| } | |
| @property uint index() nothrow pure @nogc { return _index; } | |
| @property uint flags() nothrow pure @nogc { return _flags; } | |
| @property void function() tlsctor() nothrow pure @nogc | |
| { | |
| return flags & MItlsctor ? *cast(typeof(return)*)addrOf(MItlsctor) : null; | |
| } | |
| @property void function() tlsdtor() nothrow pure @nogc | |
| { | |
| return flags & MItlsdtor ? *cast(typeof(return)*)addrOf(MItlsdtor) : null; | |
| } | |
| @property void* xgetMembers() nothrow pure @nogc | |
| { | |
| return flags & MIxgetMembers ? *cast(typeof(return)*)addrOf(MIxgetMembers) : null; | |
| } | |
| @property void function() ctor() nothrow pure @nogc | |
| { | |
| return flags & MIctor ? *cast(typeof(return)*)addrOf(MIctor) : null; | |
| } | |
| @property void function() dtor() nothrow pure @nogc | |
| { | |
| return flags & MIdtor ? *cast(typeof(return)*)addrOf(MIdtor) : null; | |
| } | |
| @property void function() ictor() nothrow pure @nogc | |
| { | |
| return flags & MIictor ? *cast(typeof(return)*)addrOf(MIictor) : null; | |
| } | |
| @property void function() unitTest() nothrow pure @nogc | |
| { | |
| return flags & MIunitTest ? *cast(typeof(return)*)addrOf(MIunitTest) : null; | |
| } | |
| @property immutable(ModuleInfo*)[] importedModules() nothrow pure @nogc | |
| { | |
| if (flags & MIimportedModules) | |
| { | |
| auto p = cast(size_t*)addrOf(MIimportedModules); | |
| return (cast(immutable(ModuleInfo*)*)(p + 1))[0 .. *p]; | |
| } | |
| return null; | |
| } | |
| @property TypeInfo_Class[] localClasses() nothrow pure @nogc | |
| { | |
| if (flags & MIlocalClasses) | |
| { | |
| auto p = cast(size_t*)addrOf(MIlocalClasses); | |
| return (cast(TypeInfo_Class*)(p + 1))[0 .. *p]; | |
| } | |
| return null; | |
| } | |
| @property string name() nothrow pure @nogc | |
| { | |
| if (true || flags & MIname) // always available for now | |
| { | |
| import core.stdc.string : strlen; | |
| auto p = cast(immutable char*)addrOf(MIname); | |
| return p[0 .. strlen(p)]; | |
| } | |
| // return null; | |
| } | |
| static int opApply(scope int delegate(ModuleInfo*) dg) | |
| { | |
| import core.internal.traits : externDFunc; | |
| alias moduleinfos_apply = externDFunc!("rt.minfo.moduleinfos_apply", | |
| int function(scope int delegate(immutable(ModuleInfo*)))); | |
| // Bugzilla 13084 - enforcing immutable ModuleInfo would break client code | |
| return moduleinfos_apply( | |
| (immutable(ModuleInfo*)m) => dg(cast(ModuleInfo*)m)); | |
| } | |
| } | |
| unittest | |
| { | |
| ModuleInfo* m1; | |
| foreach (m; ModuleInfo) | |
| { | |
| m1 = m; | |
| } | |
| } | |
| /////////////////////////////////////////////////////////////////////////////// | |
| // Throwable | |
| /////////////////////////////////////////////////////////////////////////////// | |
| /** | |
| * The base class of all thrown objects. | |
| * | |
| * All thrown objects must inherit from Throwable. Class $(D Exception), which | |
| * derives from this class, represents the category of thrown objects that are | |
| * safe to catch and handle. In principle, one should not catch Throwable | |
| * objects that are not derived from $(D Exception), as they represent | |
| * unrecoverable runtime errors. Certain runtime guarantees may fail to hold | |
| * when these errors are thrown, making it unsafe to continue execution after | |
| * catching them. | |
| */ | |
| class Throwable : Object | |
| { | |
| interface TraceInfo | |
| { | |
| int opApply(scope int delegate(ref const(char[]))) const; | |
| int opApply(scope int delegate(ref size_t, ref const(char[]))) const; | |
| string toString() const; | |
| } | |
| string msg; /// A message describing the error. | |
| /** | |
| * The _file name of the D source code corresponding with | |
| * where the error was thrown from. | |
| */ | |
| string file; | |
| /** | |
| * The _line number of the D source code corresponding with | |
| * where the error was thrown from. | |
| */ | |
| size_t line; | |
| /** | |
| * The stack trace of where the error happened. This is an opaque object | |
| * that can either be converted to $(D string), or iterated over with $(D | |
| * foreach) to extract the items in the stack trace (as strings). | |
| */ | |
| TraceInfo info; | |
| /** | |
| * A reference to the _next error in the list. This is used when a new | |
| * $(D Throwable) is thrown from inside a $(D catch) block. The originally | |
| * caught $(D Exception) will be chained to the new $(D Throwable) via this | |
| * field. | |
| */ | |
| Throwable next; | |
| @nogc @safe pure nothrow this(string msg, Throwable next = null) | |
| { | |
| this.msg = msg; | |
| this.next = next; | |
| //this.info = _d_traceContext(); | |
| } | |
| @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable next = null) | |
| { | |
| this(msg, next); | |
| this.file = file; | |
| this.line = line; | |
| //this.info = _d_traceContext(); | |
| } | |
| /** | |
| * Overrides $(D Object.toString) and returns the error message. | |
| * Internally this forwards to the $(D toString) overload that | |
| * takes a $(D_PARAM sink) delegate. | |
| */ | |
| override string toString() | |
| { | |
| string s; | |
| toString((buf) { s ~= buf; }); | |
| return s; | |
| } | |
| /** | |
| * The Throwable hierarchy uses a toString overload that takes a | |
| * $(D_PARAM _sink) delegate to avoid GC allocations, which cannot be | |
| * performed in certain error situations. Override this $(D | |
| * toString) method to customize the error message. | |
| */ | |
| void toString(scope void delegate(in char[]) sink) const | |
| { | |
| import core.internal.traits : externDFunc; | |
| alias sizeToTempString = externDFunc!("core.internal.string.unsignedToTempString", | |
| char[] function(ulong, return char[], uint) @safe pure nothrow @nogc); | |
| char[20] tmpBuff = void; | |
| sink(typeid(this).name); | |
| sink("@"); sink(file); | |
| sink("("); sink(sizeToTempString(line, tmpBuff, 10)); sink(")"); | |
| if (msg.length) | |
| { | |
| sink(": "); sink(msg); | |
| } | |
| if (info) | |
| { | |
| try | |
| { | |
| sink("\n----------------"); | |
| foreach (t; info) | |
| { | |
| sink("\n"); sink(t); | |
| } | |
| } | |
| catch (Throwable) | |
| { | |
| // ignore more errors | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * The base class of all errors that are safe to catch and handle. | |
| * | |
| * In principle, only thrown objects derived from this class are safe to catch | |
| * inside a $(D catch) block. Thrown objects not derived from Exception | |
| * represent runtime errors that should not be caught, as certain runtime | |
| * guarantees may not hold, making it unsafe to continue program execution. | |
| */ | |
| class Exception : Throwable | |
| { | |
| /** | |
| * Creates a new instance of Exception. The next parameter is used | |
| * internally and should always be $(D null) when passed by user code. | |
| * This constructor does not automatically throw the newly-created | |
| * Exception; the $(D throw) statement should be used for that purpose. | |
| */ | |
| @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) | |
| { | |
| super(msg, file, line, next); | |
| } | |
| @nogc @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) | |
| { | |
| super(msg, file, line, next); | |
| } | |
| } | |
| unittest | |
| { | |
| { | |
| auto e = new Exception("msg"); | |
| assert(e.file == __FILE__); | |
| assert(e.line == __LINE__ - 2); | |
| assert(e.next is null); | |
| assert(e.msg == "msg"); | |
| } | |
| { | |
| auto e = new Exception("msg", new Exception("It's an Exception!"), "hello", 42); | |
| assert(e.file == "hello"); | |
| assert(e.line == 42); | |
| assert(e.next !is null); | |
| assert(e.msg == "msg"); | |
| } | |
| { | |
| auto e = new Exception("msg", "hello", 42, new Exception("It's an Exception!")); | |
| assert(e.file == "hello"); | |
| assert(e.line == 42); | |
| assert(e.next !is null); | |
| assert(e.msg == "msg"); | |
| } | |
| } | |
| /** | |
| * The base class of all unrecoverable runtime errors. | |
| * | |
| * This represents the category of $(D Throwable) objects that are $(B not) | |
| * safe to catch and handle. In principle, one should not catch Error | |
| * objects, as they represent unrecoverable runtime errors. | |
| * Certain runtime guarantees may fail to hold when these errors are | |
| * thrown, making it unsafe to continue execution after catching them. | |
| */ | |
| class Error : Throwable | |
| { | |
| /** | |
| * Creates a new instance of Error. The next parameter is used | |
| * internally and should always be $(D null) when passed by user code. | |
| * This constructor does not automatically throw the newly-created | |
| * Error; the $(D throw) statement should be used for that purpose. | |
| */ | |
| @nogc @safe pure nothrow this(string msg, Throwable next = null) | |
| { | |
| super(msg, next); | |
| bypassedException = null; | |
| } | |
| @nogc @safe pure nothrow this(string msg, string file, size_t line, Throwable next = null) | |
| { | |
| super(msg, file, line, next); | |
| bypassedException = null; | |
| } | |
| /** The first $(D Exception) which was bypassed when this Error was thrown, | |
| or $(D null) if no $(D Exception)s were pending. */ | |
| Throwable bypassedException; | |
| } | |
| unittest | |
| { | |
| { | |
| auto e = new Error("msg"); | |
| assert(e.file is null); | |
| assert(e.line == 0); | |
| assert(e.next is null); | |
| assert(e.msg == "msg"); | |
| assert(e.bypassedException is null); | |
| } | |
| { | |
| auto e = new Error("msg", new Exception("It's an Exception!")); | |
| assert(e.file is null); | |
| assert(e.line == 0); | |
| assert(e.next !is null); | |
| assert(e.msg == "msg"); | |
| assert(e.bypassedException is null); | |
| } | |
| { | |
| auto e = new Error("msg", "hello", 42, new Exception("It's an Exception!")); | |
| assert(e.file == "hello"); | |
| assert(e.line == 42); | |
| assert(e.next !is null); | |
| assert(e.msg == "msg"); | |
| assert(e.bypassedException is null); | |
| } | |
| } | |
| /* Used in Exception Handling LSDA tables to 'wrap' C++ type info | |
| * so it can be distinguished from D TypeInfo | |
| */ | |
| class __cpp_type_info_ptr | |
| { | |
| void* ptr; // opaque pointer to C++ RTTI type info | |
| } | |
| extern (C) | |
| { | |
| // from druntime/src/rt/aaA.d | |
| private struct AA { void* impl; } | |
| // size_t _aaLen(in AA aa) pure nothrow @nogc; | |
| private void* _aaGetY(AA* paa, const TypeInfo_AssociativeArray ti, in size_t valsz, in void* pkey) pure nothrow; | |
| private void* _aaGetX(AA* paa, const TypeInfo_AssociativeArray ti, in size_t valsz, in void* pkey, out bool found) pure nothrow; | |
| // inout(void)* _aaGetRvalueX(inout AA aa, in TypeInfo keyti, in size_t valsz, in void* pkey); | |
| inout(void[]) _aaValues(inout AA aa, in size_t keysz, in size_t valsz, const TypeInfo tiValueArray) pure nothrow; | |
| inout(void[]) _aaKeys(inout AA aa, in size_t keysz, const TypeInfo tiKeyArray) pure nothrow; | |
| void* _aaRehash(AA* paa, in TypeInfo keyti) pure nothrow; | |
| void _aaClear(AA aa) pure nothrow; | |
| // alias _dg_t = extern(D) int delegate(void*); | |
| // int _aaApply(AA aa, size_t keysize, _dg_t dg); | |
| // alias _dg2_t = extern(D) int delegate(void*, void*); | |
| // int _aaApply2(AA aa, size_t keysize, _dg2_t dg); | |
| private struct AARange { AA impl; size_t idx; } | |
| AARange _aaRange(AA aa) pure nothrow @nogc @safe; | |
| bool _aaRangeEmpty(AARange r) pure nothrow @nogc @safe; | |
| void* _aaRangeFrontKey(AARange r) pure nothrow @nogc @safe; | |
| void* _aaRangeFrontValue(AARange r) pure nothrow @nogc @safe; | |
| void _aaRangePopFront(ref AARange r) pure nothrow @nogc @safe; | |
| int _aaEqual(in TypeInfo tiRaw, in AA aa1, in AA aa2); | |
| hash_t _aaGetHash(in AA* aa, in TypeInfo tiRaw) nothrow; | |
| /* | |
| _d_assocarrayliteralTX marked as pure, because aaLiteral can be called from pure code. | |
| This is a typesystem hole, however this is existing hole. | |
| Early compiler didn't check purity of toHash or postblit functions, if key is a UDT thus | |
| copiler allowed to create AA literal with keys, which have impure unsafe toHash methods. | |
| */ | |
| void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] values) pure; | |
| } | |
| void* aaLiteral(Key, Value)(Key[] keys, Value[] values) @trusted pure | |
| { | |
| return _d_assocarrayliteralTX(typeid(Value[Key]), *cast(void[]*)&keys, *cast(void[]*)&values); | |
| } | |
| alias AssociativeArray(Key, Value) = Value[Key]; | |
| /*********************************** | |
| * Removes all remaining keys and values from an associative array. | |
| * Params: | |
| * aa = The associative array. | |
| */ | |
| void clear(T : Value[Key], Value, Key)(T aa) | |
| { | |
| _aaClear(*cast(AA *) &aa); | |
| } | |
| /* ditto */ | |
| void clear(T : Value[Key], Value, Key)(T* aa) | |
| { | |
| _aaClear(*cast(AA *) aa); | |
| } | |
| /*********************************** | |
| * Reorganizes the associative array in place so that lookups are more | |
| * efficient. | |
| * Params: | |
| * aa = The associative array. | |
| * Returns: | |
| * The rehashed associative array. | |
| */ | |
| T rehash(T : Value[Key], Value, Key)(T aa) | |
| { | |
| _aaRehash(cast(AA*)&aa, typeid(Value[Key])); | |
| return aa; | |
| } | |
| /* ditto */ | |
| T rehash(T : Value[Key], Value, Key)(T* aa) | |
| { | |
| _aaRehash(cast(AA*)aa, typeid(Value[Key])); | |
| return *aa; | |
| } | |
| /* ditto */ | |
| T rehash(T : shared Value[Key], Value, Key)(T aa) | |
| { | |
| _aaRehash(cast(AA*)&aa, typeid(Value[Key])); | |
| return aa; | |
| } | |
| /* ditto */ | |
| T rehash(T : shared Value[Key], Value, Key)(T* aa) | |
| { | |
| _aaRehash(cast(AA*)aa, typeid(Value[Key])); | |
| return *aa; | |
| } | |
| /*********************************** | |
| * Create a new associative array of the same size and copy the contents of the | |
| * associative array into it. | |
| * Params: | |
| * aa = The associative array. | |
| */ | |
| V[K] dup(T : V[K], K, V)(T aa) | |
| { | |
| //pragma(msg, "K = ", K, ", V = ", V); | |
| // Bug10720 - check whether V is copyable | |
| static assert(is(typeof({ V v = aa[K.init]; })), | |
| "cannot call " ~ T.stringof ~ ".dup because " ~ V.stringof ~ " is not copyable"); | |
| V[K] result; | |
| //foreach (k, ref v; aa) | |
| // result[k] = v; // Bug13701 - won't work if V is not mutable | |
| ref V duplicateElem(ref K k, ref const V v) @trusted pure nothrow | |
| { | |
| import core.stdc.string : memcpy; | |
| void* pv = _aaGetY(cast(AA*)&result, typeid(V[K]), V.sizeof, &k); | |
| memcpy(pv, &v, V.sizeof); | |
| return *cast(V*)pv; | |
| } | |
| if (auto postblit = _getPostblit!V()) | |
| { | |
| foreach (k, ref v; aa) | |
| postblit(duplicateElem(k, v)); | |
| } | |
| else | |
| { | |
| foreach (k, ref v; aa) | |
| duplicateElem(k, v); | |
| } | |
| return result; | |
| } | |
| /* ditto */ | |
| V[K] dup(T : V[K], K, V)(T* aa) | |
| { | |
| return (*aa).dup; | |
| } | |
| // this should never be made public. | |
| private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe | |
| { | |
| // ensure we are dealing with a genuine AA. | |
| static if (is(const(V[K]) == const(T))) | |
| alias realAA = aa; | |
| else | |
| const(V[K]) realAA = aa; | |
| return _aaRange(() @trusted { return *cast(AA*)&realAA; } ()); | |
| } | |
| /*********************************** | |
| * Returns a forward range over the keys of the associative array. | |
| * Params: | |
| * aa = The associative array. | |
| * Returns: | |
| * A forward range. | |
| */ | |
| auto byKey(T : V[K], K, V)(T aa) pure nothrow @nogc @safe | |
| { | |
| import core.internal.traits : substInout; | |
| static struct Result | |
| { | |
| AARange r; | |
| pure nothrow @nogc: | |
| @property bool empty() @safe { return _aaRangeEmpty(r); } | |
| @property ref front() | |
| { | |
| auto p = (() @trusted => cast(substInout!K*) _aaRangeFrontKey(r)) (); | |
| return *p; | |
| } | |
| void popFront() @safe { _aaRangePopFront(r); } | |
| @property Result save() { return this; } | |
| } | |
| return Result(_aaToRange(aa)); | |
| } | |
| /* ditto */ | |
| auto byKey(T : V[K], K, V)(T* aa) pure nothrow @nogc | |
| { | |
| return (*aa).byKey(); | |
| } | |
| /*********************************** | |
| * Returns a forward range over the values of the associative array. | |
| * Params: | |
| * aa = The associative array. | |
| * Returns: | |
| * A forward range. | |
| */ | |
| auto byValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe | |
| { | |
| import core.internal.traits : substInout; | |
| static struct Result | |
| { | |
| AARange r; | |
| pure nothrow @nogc: | |
| @property bool empty() @safe { return _aaRangeEmpty(r); } | |
| @property ref front() | |
| { | |
| auto p = (() @trusted => cast(substInout!V*) _aaRangeFrontValue(r)) (); | |
| return *p; | |
| } | |
| void popFront() @safe { _aaRangePopFront(r); } | |
| @property Result save() { return this; } | |
| } | |
| return Result(_aaToRange(aa)); | |
| } | |
| /* ditto */ | |
| auto byValue(T : V[K], K, V)(T* aa) pure nothrow @nogc | |
| { | |
| return (*aa).byValue(); | |
| } | |
| /*********************************** | |
| * Returns a forward range over the key value pairs of the associative array. | |
| * Params: | |
| * aa = The associative array. | |
| * Returns: | |
| * A forward range. | |
| */ | |
| auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe | |
| { | |
| import core.internal.traits : substInout; | |
| static struct Result | |
| { | |
| AARange r; | |
| pure nothrow @nogc: | |
| @property bool empty() @safe { return _aaRangeEmpty(r); } | |
| @property auto front() | |
| { | |
| static struct Pair | |
| { | |
| // We save the pointers here so that the Pair we return | |
| // won't mutate when Result.popFront is called afterwards. | |
| private void* keyp; | |
| private void* valp; | |
| @property ref key() inout | |
| { | |
| auto p = (() @trusted => cast(substInout!K*) keyp) (); | |
| return *p; | |
| } | |
| @property ref value() inout | |
| { | |
| auto p = (() @trusted => cast(substInout!V*) valp) (); | |
| return *p; | |
| } | |
| } | |
| return Pair(_aaRangeFrontKey(r), | |
| _aaRangeFrontValue(r)); | |
| } | |
| void popFront() @safe { return _aaRangePopFront(r); } | |
| @property Result save() { return this; } | |
| } | |
| return Result(_aaToRange(aa)); | |
| } | |
| /* ditto */ | |
| auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc | |
| { | |
| return (*aa).byKeyValue(); | |
| } | |
| /*********************************** | |
| * Returns a dynamic array, the elements of which are the keys in the | |
| * associative array. | |
| * Params: | |
| * aa = The associative array. | |
| * Returns: | |
| * A dynamic array. | |
| */ | |
| Key[] keys(T : Value[Key], Value, Key)(T aa) @property | |
| { | |
| // ensure we are dealing with a genuine AA. | |
| static if (is(const(Value[Key]) == const(T))) | |
| alias realAA = aa; | |
| else | |
| const(Value[Key]) realAA = aa; | |
| auto a = cast(void[])_aaKeys(*cast(inout(AA)*)&realAA, Key.sizeof, typeid(Key[])); | |
| auto res = *cast(Key[]*)&a; | |
| _doPostblit(res); | |
| return res; | |
| } | |
| /* ditto */ | |
| Key[] keys(T : Value[Key], Value, Key)(T *aa) @property | |
| { | |
| return (*aa).keys; | |
| } | |
| @system unittest | |
| { | |
| static struct S | |
| { | |
| string str; | |
| void[][string] dict; | |
| alias dict this; | |
| } | |
| auto s = S("a"); | |
| assert(s.keys.length == 0); | |
| } | |
| /*********************************** | |
| * Returns a dynamic array, the elements of which are the values in the | |
| * associative array. | |
| * Params: | |
| * aa = The associative array. | |
| * Returns: | |
| * A dynamic array. | |
| */ | |
| Value[] values(T : Value[Key], Value, Key)(T aa) @property | |
| { | |
| // ensure we are dealing with a genuine AA. | |
| static if (is(const(Value[Key]) == const(T))) | |
| alias realAA = aa; | |
| else | |
| const(Value[Key]) realAA = aa; | |
| auto a = cast(void[])_aaValues(*cast(inout(AA)*)&realAA, Key.sizeof, Value.sizeof, typeid(Value[])); | |
| auto res = *cast(Value[]*)&a; | |
| _doPostblit(res); | |
| return res; | |
| } | |
| /* ditto */ | |
| Value[] values(T : Value[Key], Value, Key)(T *aa) @property | |
| { | |
| return (*aa).values; | |
| } | |
| @system unittest | |
| { | |
| static struct S | |
| { | |
| string str; | |
| void[][string] dict; | |
| alias dict this; | |
| } | |
| auto s = S("a"); | |
| assert(s.values.length == 0); | |
| } | |
| /*********************************** | |
| * Looks up key; if it exists returns corresponding value else evaluates and | |
| * returns defaultValue. | |
| * Params: | |
| * aa = The associative array. | |
| * key = The key. | |
| * defaultValue = The default value. | |
| * Returns: | |
| * The value. | |
| */ | |
| inout(V) get(K, V)(inout(V[K]) aa, K key, lazy inout(V) defaultValue) | |
| { | |
| auto p = key in aa; | |
| return p ? *p : defaultValue; | |
| } | |
| /* ditto */ | |
| inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue) | |
| { | |
| return (*aa).get(key, defaultValue); | |
| } | |
| /*********************************** | |
| * Looks up key; if it exists returns corresponding value else evaluates | |
| * value, adds it to the associative array and returns it. | |
| * Params: | |
| * aa = The associative array. | |
| * key = The key. | |
| * value = The required value. | |
| * Returns: | |
| * The value. | |
| */ | |
| ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init) | |
| { | |
| bool found; | |
| // if key is @safe-ly copyable, `require` can infer @safe | |
| static if (isSafeCopyable!K) | |
| { | |
| auto p = () @trusted | |
| { | |
| return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found); | |
| } (); | |
| } | |
| else | |
| { | |
| auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found); | |
| } | |
| return found ? *p : (*p = value); | |
| } | |
| // Constraints for aa update. Delegates, Functions or Functors (classes that | |
| // provide opCall) are allowed. See unittest for an example. | |
| private | |
| { | |
| template isCreateOperation(C, V) | |
| { | |
| static if (is(C : V delegate()) || is(C : V function())) | |
| enum bool isCreateOperation = true; | |
| else static if (isCreateOperation!(typeof(&C.opCall), V)) | |
| enum bool isCreateOperation = true; | |
| else | |
| enum bool isCreateOperation = false; | |
| } | |
| template isUpdateOperation(U, V) | |
| { | |
| static if (is(U : V delegate(ref V)) || is(U : V function(ref V))) | |
| enum bool isUpdateOperation = true; | |
| else static if (isUpdateOperation!(typeof(&U.opCall), V)) | |
| enum bool isUpdateOperation = true; | |
| else | |
| enum bool isUpdateOperation = false; | |
| } | |
| } | |
| // Tests whether T can be @safe-ly copied. Use a union to exclude destructor from the test. | |
| private enum bool isSafeCopyable(T) = is(typeof(() @safe { union U { T x; } T *x; auto u = U(*x); })); | |
| /*********************************** | |
| * Looks up key; if it exists applies the update delegate else evaluates the | |
| * create delegate and adds it to the associative array | |
| * Params: | |
| * aa = The associative array. | |
| * key = The key. | |
| * create = The delegate to apply on create. | |
| * update = The delegate to apply on update. | |
| */ | |
| void update(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update) | |
| if (isCreateOperation!(C, V) && isUpdateOperation!(U, V)) | |
| { | |
| bool found; | |
| // if key is @safe-ly copyable, `update` may infer @safe | |
| static if (isSafeCopyable!K) | |
| { | |
| auto p = () @trusted | |
| { | |
| return cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found); | |
| } (); | |
| } | |
| else | |
| { | |
| auto p = cast(V*) _aaGetX(cast(AA*) &aa, typeid(V[K]), V.sizeof, &key, found); | |
| } | |
| if (!found) | |
| *p = create(); | |
| else | |
| *p = update(*p); | |
| } | |
| unittest | |
| { | |
| static struct S | |
| { | |
| int x; | |
| @nogc nothrow pure: | |
| this(this) @system {} | |
| @safe const: | |
| // stubs | |
| bool opEquals(S rhs) { assert(0); } | |
| size_t toHash() { assert(0); } | |
| } | |
| int[string] aai; | |
| static assert(is(typeof(() @safe { aai.require("a", 1234); }))); | |
| static assert(is(typeof(() @safe { aai.update("a", { return 1234; }, (ref int x) { x++; return x; }); }))); | |
| S[string] aas; | |
| static assert(is(typeof(() { aas.require("a", S(1234)); }))); | |
| static assert(is(typeof(() { aas.update("a", { return S(1234); }, (ref S s) { s.x++; return s; }); }))); | |
| static assert(!is(typeof(() @safe { aas.update("a", { return S(1234); }, (ref S s) { s.x++; return s; }); }))); | |
| int[S] aais; | |
| static assert(is(typeof(() { aais.require(S(1234), 1234); }))); | |
| static assert(is(typeof(() { aais.update(S(1234), { return 1234; }, (ref int x) { x++; return x; }); }))); | |
| static assert(!is(typeof(() @safe { aais.require(S(1234), 1234); }))); | |
| static assert(!is(typeof(() @safe { aais.update(S(1234), { return 1234; }, (ref int x) { x++; return x; }); }))); | |
| } | |
| private 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(); | |
| } | |
| private 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 and explicitly undocumented | |
| void _postblitRecurse(S)(ref S s) | |
| if (is(S == struct)) | |
| { | |
| static if (__traits(hasMember, S, "__xpostblit") && | |
| // Bugzilla 14746: Check that it's the exact member of S. | |
| __traits(isSame, S, __traits(parent, s.__xpostblit))) | |
| s.__xpostblit(); | |
| } | |
| // Ditto | |
| void _postblitRecurse(E, size_t n)(ref E[n] arr) | |
| { | |
| import core.internal.traits : hasElaborateCopyConstructor; | |
| static if (hasElaborateCopyConstructor!E) | |
| { | |
| size_t i; | |
| scope(failure) | |
| { | |
| for (; i != 0; --i) | |
| { | |
| _destructRecurse(arr[i - 1]); // What to do if this throws? | |
| } | |
| } | |
| for (i = 0; i < arr.length; ++i) | |
| _postblitRecurse(arr[i]); | |
| } | |
| } | |
| // Test destruction/postblit order | |
| @safe nothrow pure unittest | |
| { | |
| string[] order; | |
| struct InnerTop | |
| { | |
| ~this() @safe nothrow pure | |
| { | |
| order ~= "destroy inner top"; | |
| } | |
| this(this) @safe nothrow pure | |
| { | |
| order ~= "copy inner top"; | |
| } | |
| } | |
| struct InnerMiddle {} | |
| version (none) // https://issues.dlang.org/show_bug.cgi?id=14242 | |
| struct InnerElement | |
| { | |
| static char counter = '1'; | |
| ~this() @safe nothrow pure | |
| { | |
| order ~= "destroy inner element #" ~ counter++; | |
| } | |
| this(this) @safe nothrow pure | |
| { | |
| order ~= "copy inner element #" ~ counter++; | |
| } | |
| } | |
| struct InnerBottom | |
| { | |
| ~this() @safe nothrow pure | |
| { | |
| order ~= "destroy inner bottom"; | |
| } | |
| this(this) @safe nothrow pure | |
| { | |
| order ~= "copy inner bottom"; | |
| } | |
| } | |
| struct S | |
| { | |
| char[] s; | |
| InnerTop top; | |
| InnerMiddle middle; | |
| version (none) InnerElement[3] array; // https://issues.dlang.org/show_bug.cgi?id=14242 | |
| int a; | |
| InnerBottom bottom; | |
| ~this() @safe nothrow pure { order ~= "destroy outer"; } | |
| this(this) @safe nothrow pure { order ~= "copy outer"; } | |
| } | |
| string[] destructRecurseOrder; | |
| { | |
| S s; | |
| _destructRecurse(s); | |
| destructRecurseOrder = order; | |
| order = null; | |
| } | |
| assert(order.length); | |
| assert(destructRecurseOrder == order); | |
| order = null; | |
| S s; | |
| _postblitRecurse(s); | |
| assert(order.length); | |
| auto postblitRecurseOrder = order; | |
| order = null; | |
| S s2 = s; | |
| assert(order.length); | |
| assert(postblitRecurseOrder == order); | |
| } | |
| // Test static struct | |
| nothrow @safe @nogc unittest | |
| { | |
| static int i = 0; | |
| static struct S { ~this() nothrow @safe @nogc { i = 42; } } | |
| S s; | |
| _destructRecurse(s); | |
| assert(i == 42); | |
| } | |
| unittest | |
| { | |
| // Bugzilla 14746 | |
| static struct HasDtor | |
| { | |
| ~this() { assert(0); } | |
| } | |
| static struct Owner | |
| { | |
| HasDtor* ptr; | |
| alias ptr this; | |
| } | |
| Owner o; | |
| assert(o.ptr is null); | |
| destroy(o); // must not reach in HasDtor.__dtor() | |
| } | |
| unittest | |
| { | |
| // Bugzilla 14746 | |
| static struct HasPostblit | |
| { | |
| this(this) { assert(0); } | |
| } | |
| static struct Owner | |
| { | |
| HasPostblit* ptr; | |
| alias ptr this; | |
| } | |
| Owner o; | |
| assert(o.ptr is null); | |
| _postblitRecurse(o); // must not reach in HasPostblit.__postblit() | |
| } | |
| // Test handling of fixed-length arrays | |
| // Separate from first test because of https://issues.dlang.org/show_bug.cgi?id=14242 | |
| unittest | |
| { | |
| string[] order; | |
| struct S | |
| { | |
| char id; | |
| this(this) | |
| { | |
| order ~= "copy #" ~ id; | |
| } | |
| ~this() | |
| { | |
| order ~= "destroy #" ~ id; | |
| } | |
| } | |
| string[] destructRecurseOrder; | |
| { | |
| S[3] arr = [S('1'), S('2'), S('3')]; | |
| _destructRecurse(arr); | |
| destructRecurseOrder = order; | |
| order = null; | |
| } | |
| assert(order.length); | |
| assert(destructRecurseOrder == order); | |
| order = null; | |
| S[3] arr = [S('1'), S('2'), S('3')]; | |
| _postblitRecurse(arr); | |
| assert(order.length); | |
| auto postblitRecurseOrder = order; | |
| order = null; | |
| auto arrCopy = arr; | |
| assert(order.length); | |
| assert(postblitRecurseOrder == order); | |
| } | |
| // Test handling of failed postblit | |
| // Not nothrow or @safe because of https://issues.dlang.org/show_bug.cgi?id=14242 | |
| /+ nothrow @safe +/ unittest | |
| { | |
| static class FailedPostblitException : Exception { this() nothrow @safe { super(null); } } | |
| static string[] order; | |
| static struct Inner | |
| { | |
| char id; | |
| @safe: | |
| this(this) | |
| { | |
| order ~= "copy inner #" ~ id; | |
| if (id == '2') | |
| throw new FailedPostblitException(); | |
| } | |
| ~this() nothrow | |
| { | |
| order ~= "destroy inner #" ~ id; | |
| } | |
| } | |
| static struct Outer | |
| { | |
| Inner inner1, inner2, inner3; | |
| nothrow @safe: | |
| this(char first, char second, char third) | |
| { | |
| inner1 = Inner(first); | |
| inner2 = Inner(second); | |
| inner3 = Inner(third); | |
| } | |
| this(this) | |
| { | |
| order ~= "copy outer"; | |
| } | |
| ~this() | |
| { | |
| order ~= "destroy outer"; | |
| } | |
| } | |
| auto outer = Outer('1', '2', '3'); | |
| try _postblitRecurse(outer); | |
| catch (FailedPostblitException) {} | |
| catch (Exception) assert(false); | |
| auto postblitRecurseOrder = order; | |
| order = null; | |
| try auto copy = outer; | |
| catch (FailedPostblitException) {} | |
| catch (Exception) assert(false); | |
| assert(postblitRecurseOrder == order); | |
| order = null; | |
| Outer[3] arr = [Outer('1', '1', '1'), Outer('1', '2', '3'), Outer('3', '3', '3')]; | |
| try _postblitRecurse(arr); | |
| catch (FailedPostblitException) {} | |
| catch (Exception) assert(false); | |
| postblitRecurseOrder = order; | |
| order = null; | |
| try auto arrCopy = arr; | |
| catch (FailedPostblitException) {} | |
| catch (Exception) assert(false); | |
| assert(postblitRecurseOrder == order); | |
| } | |
| /++ | |
| Destroys the given object and puts it in an invalid state. It's used to | |
| _destroy an object so that any cleanup which its destructor or finalizer | |
| does is done and so that it no longer references any other objects. It does | |
| $(I not) initiate a GC cycle or free any GC memory. | |
| +/ | |
| void destroy(T)(T obj) if (is(T == class)) | |
| { | |
| rt_finalize(cast(void*)obj); | |
| } | |
| /// ditto | |
| void destroy(T)(T obj) if (is(T == interface)) | |
| { | |
| destroy(cast(Object)obj); | |
| } | |
| version (unittest) unittest | |
| { | |
| interface I { } | |
| { | |
| class A: I { string s = "A"; this() {} } | |
| auto a = new A, b = new A; | |
| a.s = b.s = "asd"; | |
| destroy(a); | |
| assert(a.s == "A"); | |
| I i = b; | |
| destroy(i); | |
| assert(b.s == "A"); | |
| } | |
| { | |
| static bool destroyed = false; | |
| class B: I | |
| { | |
| string s = "B"; | |
| this() {} | |
| ~this() | |
| { | |
| destroyed = true; | |
| } | |
| } | |
| auto a = new B, b = new B; | |
| a.s = b.s = "asd"; | |
| destroy(a); | |
| assert(destroyed); | |
| assert(a.s == "B"); | |
| destroyed = false; | |
| I i = b; | |
| destroy(i); | |
| assert(destroyed); | |
| assert(b.s == "B"); | |
| } | |
| // this test is invalid now that the default ctor is not run after clearing | |
| version (none) | |
| { | |
| class C | |
| { | |
| string s; | |
| this() | |
| { | |
| s = "C"; | |
| } | |
| } | |
| auto a = new C; | |
| a.s = "asd"; | |
| destroy(a); | |
| assert(a.s == "C"); | |
| } | |
| } | |
| /// ditto | |
| void destroy(T)(ref T obj) if (is(T == struct)) | |
| { | |
| _destructRecurse(obj); | |
| () @trusted { | |
| auto buf = (cast(ubyte*) &obj)[0 .. T.sizeof]; | |
| auto init = cast(ubyte[])typeid(T).initializer(); | |
| if (init.ptr is null) // null ptr means initialize to 0s | |
| buf[] = 0; | |
| else | |
| buf[] = init[]; | |
| } (); | |
| } | |
| version (unittest) nothrow @safe @nogc unittest | |
| { | |
| { | |
| struct A { string s = "A"; } | |
| A a; | |
| a.s = "asd"; | |
| destroy(a); | |
| assert(a.s == "A"); | |
| } | |
| { | |
| static int destroyed = 0; | |
| struct C | |
| { | |
| string s = "C"; | |
| ~this() nothrow @safe @nogc | |
| { | |
| destroyed ++; | |
| } | |
| } | |
| struct B | |
| { | |
| C c; | |
| string s = "B"; | |
| ~this() nothrow @safe @nogc | |
| { | |
| destroyed ++; | |
| } | |
| } | |
| B a; | |
| a.s = "asd"; | |
| a.c.s = "jkl"; | |
| destroy(a); | |
| assert(destroyed == 2); | |
| assert(a.s == "B"); | |
| assert(a.c.s == "C" ); | |
| } | |
| } | |
| /// ditto | |
| void destroy(T : U[n], U, size_t n)(ref T obj) if (!is(T == struct)) | |
| { | |
| foreach_reverse (ref e; obj[]) | |
| destroy(e); | |
| } | |
| version (unittest) unittest | |
| { | |
| int[2] a; | |
| a[0] = 1; | |
| a[1] = 2; | |
| destroy(a); | |
| assert(a == [ 0, 0 ]); | |
| } | |
| unittest | |
| { | |
| static struct vec2f { | |
| float[2] values; | |
| alias values this; | |
| } | |
| vec2f v; | |
| destroy!vec2f(v); | |
| } | |
| unittest | |
| { | |
| // Bugzilla 15009 | |
| static string op; | |
| static struct S | |
| { | |
| int x; | |
| this(int x) { op ~= "C" ~ cast(char)('0'+x); this.x = x; } | |
| this(this) { op ~= "P" ~ cast(char)('0'+x); } | |
| ~this() { op ~= "D" ~ cast(char)('0'+x); } | |
| } | |
| { | |
| S[2] a1 = [S(1), S(2)]; | |
| op = ""; | |
| } | |
| assert(op == "D2D1"); // built-in scope destruction | |
| { | |
| S[2] a1 = [S(1), S(2)]; | |
| op = ""; | |
| destroy(a1); | |
| assert(op == "D2D1"); // consistent with built-in behavior | |
| } | |
| { | |
| S[2][2] a2 = [[S(1), S(2)], [S(3), S(4)]]; | |
| op = ""; | |
| } | |
| assert(op == "D4D3D2D1"); | |
| { | |
| S[2][2] a2 = [[S(1), S(2)], [S(3), S(4)]]; | |
| op = ""; | |
| destroy(a2); | |
| assert(op == "D4D3D2D1", op); | |
| } | |
| } | |
| /// ditto | |
| void destroy(T)(ref T obj) | |
| if (!is(T == struct) && !is(T == interface) && !is(T == class) && !_isStaticArray!T) | |
| { | |
| obj = T.init; | |
| } | |
| template _isStaticArray(T : U[N], U, size_t N) | |
| { | |
| enum bool _isStaticArray = true; | |
| } | |
| template _isStaticArray(T) | |
| { | |
| enum bool _isStaticArray = false; | |
| } | |
| version (unittest) unittest | |
| { | |
| { | |
| int a = 42; | |
| destroy(a); | |
| assert(a == 0); | |
| } | |
| { | |
| float a = 42; | |
| destroy(a); | |
| assert(isnan(a)); | |
| } | |
| } | |
| version (unittest) | |
| { | |
| private bool isnan(float x) | |
| { | |
| return x != x; | |
| } | |
| } | |
| private | |
| { | |
| extern (C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow; | |
| extern (C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void *arrptr) pure nothrow; | |
| } | |
| /** | |
| * (Property) Gets the current _capacity of a slice. The _capacity is the size | |
| * that the slice can grow to before the underlying array must be | |
| * reallocated or extended. | |
| * | |
| * If an append must reallocate a slice with no possibility of extension, then | |
| * `0` is returned. This happens when the slice references a static array, or | |
| * if another slice references elements past the end of the current slice. | |
| * | |
| * Note: The _capacity of a slice may be impacted by operations on other slices. | |
| */ | |
| @property size_t capacity(T)(T[] arr) pure nothrow @trusted | |
| { | |
| return _d_arraysetcapacity(typeid(T[]), 0, cast(void *)&arr); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| //Static array slice: no capacity | |
| int[4] sarray = [1, 2, 3, 4]; | |
| int[] slice = sarray[]; | |
| assert(sarray.capacity == 0); | |
| //Appending to slice will reallocate to a new array | |
| slice ~= 5; | |
| assert(slice.capacity >= 5); | |
| //Dynamic array slices | |
| int[] a = [1, 2, 3, 4]; | |
| int[] b = a[1 .. $]; | |
| int[] c = a[1 .. $ - 1]; | |
| debug(SENTINEL) {} else // non-zero capacity very much depends on the array and GC implementation | |
| { | |
| assert(a.capacity != 0); | |
| assert(a.capacity == b.capacity + 1); //both a and b share the same tail | |
| } | |
| assert(c.capacity == 0); //an append to c must relocate c. | |
| } | |
| /** | |
| * Reserves capacity for a slice. The capacity is the size | |
| * that the slice can grow to before the underlying array must be | |
| * reallocated or extended. | |
| * | |
| * Returns: The new capacity of the array (which may be larger than | |
| * the requested capacity). | |
| */ | |
| size_t reserve(T)(ref T[] arr, size_t newcapacity) pure nothrow @trusted | |
| { | |
| return _d_arraysetcapacity(typeid(T[]), newcapacity, cast(void *)&arr); | |
| } | |
| /// | |
| unittest | |
| { | |
| //Static array slice: no capacity. Reserve relocates. | |
| int[4] sarray = [1, 2, 3, 4]; | |
| int[] slice = sarray[]; | |
| auto u = slice.reserve(8); | |
| assert(u >= 8); | |
| assert(sarray.ptr !is slice.ptr); | |
| assert(slice.capacity == u); | |
| //Dynamic array slices | |
| int[] a = [1, 2, 3, 4]; | |
| a.reserve(8); //prepare a for appending 4 more items | |
| auto p = a.ptr; | |
| u = a.capacity; | |
| a ~= [5, 6, 7, 8]; | |
| assert(p == a.ptr); //a should not have been reallocated | |
| assert(u == a.capacity); //a should not have been extended | |
| } | |
| // Issue 6646: should be possible to use array.reserve from SafeD. | |
| @safe unittest | |
| { | |
| int[] a; | |
| a.reserve(10); | |
| } | |
| /** | |
| * Assume that it is safe to append to this array. Appends made to this array | |
| * after calling this function may append in place, even if the array was a | |
| * slice of a larger array to begin with. | |
| * | |
| * Use this only when it is certain there are no elements in use beyond the | |
| * array in the memory block. If there are, those elements will be | |
| * overwritten by appending to this array. | |
| * | |
| * Warning: Calling this function, and then using references to data located after the | |
| * given array results in undefined behavior. | |
| * | |
| * Returns: | |
| * The input is returned. | |
| */ | |
| auto ref inout(T[]) assumeSafeAppend(T)(auto ref inout(T[]) arr) nothrow | |
| { | |
| _d_arrayshrinkfit(typeid(T[]), *(cast(void[]*)&arr)); | |
| return arr; | |
| } | |
| /// | |
| unittest | |
| { | |
| int[] a = [1, 2, 3, 4]; | |
| // Without assumeSafeAppend. Appending relocates. | |
| int[] b = a [0 .. 3]; | |
| b ~= 5; | |
| assert(a.ptr != b.ptr); | |
| debug(SENTINEL) {} else | |
| { | |
| // With assumeSafeAppend. Appending overwrites. | |
| int[] c = a [0 .. 3]; | |
| c.assumeSafeAppend() ~= 5; | |
| assert(a.ptr == c.ptr); | |
| } | |
| } | |
| unittest | |
| { | |
| int[] arr; | |
| auto newcap = arr.reserve(2000); | |
| assert(newcap >= 2000); | |
| assert(newcap == arr.capacity); | |
| auto ptr = arr.ptr; | |
| foreach (i; 0..2000) | |
| arr ~= i; | |
| assert(ptr == arr.ptr); | |
| arr = arr[0..1]; | |
| arr.assumeSafeAppend(); | |
| arr ~= 5; | |
| assert(ptr == arr.ptr); | |
| } | |
| unittest | |
| { | |
| int[] arr = [1, 2, 3]; | |
| void foo(ref int[] i) | |
| { | |
| i ~= 5; | |
| } | |
| arr = arr[0 .. 2]; | |
| foo(assumeSafeAppend(arr)); //pass by ref | |
| assert(arr[]==[1, 2, 5]); | |
| arr = arr[0 .. 1].assumeSafeAppend(); //pass by value | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=10574 | |
| unittest | |
| { | |
| int[] a; | |
| immutable(int[]) b; | |
| auto a2 = &assumeSafeAppend(a); | |
| auto b2 = &assumeSafeAppend(b); | |
| auto a3 = assumeSafeAppend(a[]); | |
| auto b3 = assumeSafeAppend(b[]); | |
| assert(is(typeof(*a2) == int[])); | |
| assert(is(typeof(*b2) == immutable(int[]))); | |
| assert(is(typeof(a3) == int[])); | |
| assert(is(typeof(b3) == immutable(int[]))); | |
| } | |
| version (none) | |
| { | |
| // enforce() copied from Phobos std.contracts for destroy(), left out until | |
| // we decide whether to use it. | |
| T _enforce(T, string file = __FILE__, int line = __LINE__) | |
| (T value, lazy const(char)[] msg = null) | |
| { | |
| if (!value) bailOut(file, line, msg); | |
| return value; | |
| } | |
| T _enforce(T, string file = __FILE__, int line = __LINE__) | |
| (T value, scope void delegate() dg) | |
| { | |
| if (!value) dg(); | |
| return value; | |
| } | |
| T _enforce(T)(T value, lazy Exception ex) | |
| { | |
| if (!value) throw ex(); | |
| return value; | |
| } | |
| private void _bailOut(string file, int line, in char[] msg) | |
| { | |
| char[21] buf; | |
| throw new Exception(cast(string)(file ~ "(" ~ ulongToString(buf[], line) ~ "): " ~ (msg ? msg : "Enforcement failed"))); | |
| } | |
| } | |
| /*************************************** | |
| * Helper function used to see if two containers of different | |
| * types have the same contents in the same sequence. | |
| */ | |
| bool _ArrayEq(T1, T2)(T1[] a1, T2[] a2) | |
| { | |
| if (a1.length != a2.length) | |
| return false; | |
| // This is function is used as a compiler intrinsic and explicitly written | |
| // in a lowered flavor to use as few CTFE instructions as possible. | |
| size_t idx = 0; | |
| immutable length = a1.length; | |
| for (;idx < length;++idx) | |
| { | |
| if (a1[idx] != a2[idx]) | |
| return false; | |
| } | |
| return true; | |
| } | |
| version (D_Ddoc) | |
| { | |
| // This lets DDoc produce better documentation. | |
| /** | |
| Calculates the hash value of `arg` with an optional `seed` initial value. | |
| The result might not be equal to `typeid(T).getHash(&arg)`. | |
| Params: | |
| arg = argument to calculate the hash value of | |
| seed = optional `seed` value (may be used for hash chaining) | |
| Return: calculated hash value of `arg` | |
| */ | |
| size_t hashOf(T)(auto ref T arg, size_t seed) | |
| { | |
| static import core.internal.hash; | |
| return core.internal.hash.hashOf(arg, seed); | |
| } | |
| /// ditto | |
| size_t hashOf(T)(auto ref T arg) | |
| { | |
| static import core.internal.hash; | |
| return core.internal.hash.hashOf(arg); | |
| } | |
| } | |
| else | |
| { | |
| public import core.internal.hash : hashOf; | |
| } | |
| unittest | |
| { | |
| // Issue # 16654 / 16764 | |
| auto a = [1]; | |
| auto b = a.dup; | |
| assert(hashOf(a) == hashOf(b)); | |
| } | |
| bool _xopEquals(in void*, in void*) | |
| { | |
| throw new Error("TypeInfo.equals is not implemented"); | |
| } | |
| bool _xopCmp(in void*, in void*) | |
| { | |
| throw new Error("TypeInfo.compare is not implemented"); | |
| } | |
| void __ctfeWrite(const string s) @nogc @safe pure nothrow {} | |
| /****************************************** | |
| * Create RTInfo for type T | |
| */ | |
| template RTInfo(T) | |
| { | |
| enum RTInfo = null; | |
| } | |
| // lhs == rhs lowers to __equals(lhs, rhs) for dynamic arrays | |
| bool __equals(T1, T2)(T1[] lhs, T2[] rhs) | |
| { | |
| import core.internal.traits : Unqual; | |
| alias U1 = Unqual!T1; | |
| alias U2 = Unqual!T2; | |
| static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; } | |
| static @trusted R trustedCast(R, S)(S[] r) { return cast(R) r; } | |
| if (lhs.length != rhs.length) | |
| return false; | |
| if (lhs.length == 0 && rhs.length == 0) | |
| return true; | |
| static if (is(U1 == void) && is(U2 == void)) | |
| { | |
| return __equals(trustedCast!(ubyte[])(lhs), trustedCast!(ubyte[])(rhs)); | |
| } | |
| else static if (is(U1 == void)) | |
| { | |
| return __equals(trustedCast!(ubyte[])(lhs), rhs); | |
| } | |
| else static if (is(U2 == void)) | |
| { | |
| return __equals(lhs, trustedCast!(ubyte[])(rhs)); | |
| } | |
| else static if (!is(U1 == U2)) | |
| { | |
| // This should replace src/object.d _ArrayEq which | |
| // compares arrays of different types such as long & int, | |
| // char & wchar. | |
| // Compiler lowers to __ArrayEq in dmd/src/opover.d | |
| foreach (const u; 0 .. lhs.length) | |
| { | |
| if (at(lhs, u) != at(rhs, u)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| else static if (__traits(isIntegral, U1)) | |
| { | |
| if (!__ctfe) | |
| { | |
| import core.stdc.string : memcmp; | |
| return () @trusted { return memcmp(cast(void*)lhs.ptr, cast(void*)rhs.ptr, lhs.length * U1.sizeof) == 0; }(); | |
| } | |
| else | |
| { | |
| foreach (const u; 0 .. lhs.length) | |
| { | |
| if (at(lhs, u) != at(rhs, u)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| } | |
| else | |
| { | |
| foreach (const u; 0 .. lhs.length) | |
| { | |
| static if (__traits(compiles, __equals(at(lhs, u), at(rhs, u)))) | |
| { | |
| if (!__equals(at(lhs, u), at(rhs, u))) | |
| return false; | |
| } | |
| else static if (__traits(isFloating, U1)) | |
| { | |
| if (at(lhs, u) != at(rhs, u)) | |
| return false; | |
| } | |
| else static if (is(U1 : Object) && is(U2 : Object)) | |
| { | |
| if (!(cast(Object)at(lhs, u) is cast(Object)at(rhs, u) | |
| || at(lhs, u) && (cast(Object)at(lhs, u)).opEquals(cast(Object)at(rhs, u)))) | |
| return false; | |
| } | |
| else static if (__traits(hasMember, U1, "opEquals")) | |
| { | |
| if (!at(lhs, u).opEquals(at(rhs, u))) | |
| return false; | |
| } | |
| else static if (is(U1 == delegate)) | |
| { | |
| if (at(lhs, u) != at(rhs, u)) | |
| return false; | |
| } | |
| else static if (is(U1 == U11*, U11)) | |
| { | |
| if (at(lhs, u) != at(rhs, u)) | |
| return false; | |
| } | |
| else | |
| { | |
| if (at(lhs, u).tupleof != at(rhs, u).tupleof) | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| } | |
| unittest { | |
| assert(__equals([], [])); | |
| assert(!__equals([1, 2], [1, 2, 3])); | |
| } | |
| unittest | |
| { | |
| struct A | |
| { | |
| int a; | |
| } | |
| auto arr1 = [A(0), A(2)]; | |
| auto arr2 = [A(0), A(1)]; | |
| auto arr3 = [A(0), A(1)]; | |
| assert(arr1 != arr2); | |
| assert(arr2 == arr3); | |
| } | |
| unittest | |
| { | |
| struct A | |
| { | |
| int a; | |
| int b; | |
| bool opEquals(const A other) | |
| { | |
| return this.a == other.b && this.b == other.a; | |
| } | |
| } | |
| auto arr1 = [A(1, 0), A(0, 1)]; | |
| auto arr2 = [A(1, 0), A(0, 1)]; | |
| auto arr3 = [A(0, 1), A(1, 0)]; | |
| assert(arr1 != arr2); | |
| assert(arr2 == arr3); | |
| } | |
| // Compare class and interface objects for ordering. | |
| private int __cmp(Obj)(Obj lhs, Obj rhs) | |
| if (is(Obj : Object)) | |
| { | |
| if (lhs is rhs) | |
| return 0; | |
| // Regard null references as always being "less than" | |
| if (!lhs) | |
| return -1; | |
| if (!rhs) | |
| return 1; | |
| return lhs.opCmp(rhs); | |
| } | |
| int __cmp(T)(const T[] lhs, const T[] rhs) @trusted | |
| if (__traits(isScalar, T)) | |
| { | |
| // Compute U as the implementation type for T | |
| static if (is(T == ubyte) || is(T == void) || is(T == bool)) | |
| alias U = char; | |
| else static if (is(T == wchar)) | |
| alias U = ushort; | |
| else static if (is(T == dchar)) | |
| alias U = uint; | |
| else static if (is(T == ifloat)) | |
| alias U = float; | |
| else static if (is(T == idouble)) | |
| alias U = double; | |
| else static if (is(T == ireal)) | |
| alias U = real; | |
| else | |
| alias U = T; | |
| static if (is(U == char)) | |
| { | |
| import core.internal.string : dstrcmp; | |
| return dstrcmp(cast(char[]) lhs, cast(char[]) rhs); | |
| } | |
| else static if (!is(U == T)) | |
| { | |
| // Reuse another implementation | |
| return __cmp(cast(U[]) lhs, cast(U[]) rhs); | |
| } | |
| else | |
| { | |
| immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length; | |
| foreach (const u; 0 .. len) | |
| { | |
| static if (__traits(isFloating, T)) | |
| { | |
| immutable a = lhs.ptr[u], b = rhs.ptr[u]; | |
| static if (is(T == cfloat) || is(T == cdouble) | |
| || is(T == creal)) | |
| { | |
| // Use rt.cmath2._Ccmp instead ? | |
| auto r = (a.re > b.re) - (a.re < b.re); | |
| if (!r) r = (a.im > b.im) - (a.im < b.im); | |
| } | |
| else | |
| { | |
| const r = (a > b) - (a < b); | |
| } | |
| if (r) return r; | |
| } | |
| else if (lhs.ptr[u] != rhs.ptr[u]) | |
| return lhs.ptr[u] < rhs.ptr[u] ? -1 : 1; | |
| } | |
| return lhs.length < rhs.length ? -1 : (lhs.length > rhs.length); | |
| } | |
| } | |
| // This function is called by the compiler when dealing with array | |
| // comparisons in the semantic analysis phase of CmpExp. The ordering | |
| // comparison is lowered to a call to this template. | |
| int __cmp(T1, T2)(T1[] s1, T2[] s2) | |
| if (!__traits(isScalar, T1) && !__traits(isScalar, T2)) | |
| { | |
| import core.internal.traits : Unqual; | |
| alias U1 = Unqual!T1; | |
| alias U2 = Unqual!T2; | |
| static if (is(U1 == void) && is(U2 == void)) | |
| static @trusted ref inout(ubyte) at(inout(void)[] r, size_t i) { return (cast(inout(ubyte)*) r.ptr)[i]; } | |
| else | |
| static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; } | |
| // All unsigned byte-wide types = > dstrcmp | |
| immutable len = s1.length <= s2.length ? s1.length : s2.length; | |
| foreach (const u; 0 .. len) | |
| { | |
| static if (__traits(compiles, __cmp(at(s1, u), at(s2, u)))) | |
| { | |
| auto c = __cmp(at(s1, u), at(s2, u)); | |
| if (c != 0) | |
| return c; | |
| } | |
| else static if (__traits(compiles, at(s1, u).opCmp(at(s2, u)))) | |
| { | |
| auto c = at(s1, u).opCmp(at(s2, u)); | |
| if (c != 0) | |
| return c; | |
| } | |
| else static if (__traits(compiles, at(s1, u) < at(s2, u))) | |
| { | |
| if (at(s1, u) != at(s2, u)) | |
| return at(s1, u) < at(s2, u) ? -1 : 1; | |
| } | |
| else | |
| { | |
| // TODO: fix this legacy bad behavior, see | |
| // https://issues.dlang.org/show_bug.cgi?id=17244 | |
| static assert(is(U1 == U2), "Internal error."); | |
| import core.stdc.string : memcmp; | |
| auto c = (() @trusted => memcmp(&at(s1, u), &at(s2, u), U1.sizeof))(); | |
| if (c != 0) | |
| return c; | |
| } | |
| } | |
| return s1.length < s2.length ? -1 : (s1.length > s2.length); | |
| } | |
| // integral types | |
| @safe unittest | |
| { | |
| void compareMinMax(T)() | |
| { | |
| T[2] a = [T.max, T.max]; | |
| T[2] b = [T.min, T.min]; | |
| assert(__cmp(a, b) > 0); | |
| assert(__cmp(b, a) < 0); | |
| } | |
| compareMinMax!int; | |
| compareMinMax!uint; | |
| compareMinMax!long; | |
| compareMinMax!ulong; | |
| compareMinMax!short; | |
| compareMinMax!ushort; | |
| compareMinMax!byte; | |
| compareMinMax!dchar; | |
| compareMinMax!wchar; | |
| } | |
| // char types (dstrcmp) | |
| @safe unittest | |
| { | |
| void compareMinMax(T)() | |
| { | |
| T[2] a = [T.max, T.max]; | |
| T[2] b = [T.min, T.min]; | |
| assert(__cmp(a, b) > 0); | |
| assert(__cmp(b, a) < 0); | |
| } | |
| compareMinMax!ubyte; | |
| compareMinMax!bool; | |
| compareMinMax!char; | |
| compareMinMax!(const char); | |
| string s1 = "aaaa"; | |
| string s2 = "bbbb"; | |
| assert(__cmp(s2, s1) > 0); | |
| assert(__cmp(s1, s2) < 0); | |
| } | |
| // fp types | |
| @safe unittest | |
| { | |
| void compareMinMax(T)() | |
| { | |
| T[2] a = [T.max, T.max]; | |
| T[2] b = [T.min_normal, T.min_normal]; | |
| T[2] c = [T.max, T.min_normal]; | |
| T[1] d = [T.max]; | |
| assert(__cmp(a, b) > 0); | |
| assert(__cmp(b, a) < 0); | |
| assert(__cmp(a, c) > 0); | |
| assert(__cmp(a, d) > 0); | |
| assert(__cmp(d, c) < 0); | |
| assert(__cmp(c, c) == 0); | |
| } | |
| compareMinMax!real; | |
| compareMinMax!float; | |
| compareMinMax!double; | |
| compareMinMax!ireal; | |
| compareMinMax!ifloat; | |
| compareMinMax!idouble; | |
| compareMinMax!creal; | |
| //compareMinMax!cfloat; | |
| compareMinMax!cdouble; | |
| // qualifiers | |
| compareMinMax!(const real); | |
| compareMinMax!(immutable real); | |
| } | |
| // void[] | |
| @safe unittest | |
| { | |
| void[] a; | |
| const(void)[] b; | |
| (() @trusted | |
| { | |
| a = cast(void[]) "bb"; | |
| b = cast(const(void)[]) "aa"; | |
| })(); | |
| assert(__cmp(a, b) > 0); | |
| assert(__cmp(b, a) < 0); | |
| } | |
| // arrays of arrays with mixed modifiers | |
| @safe unittest | |
| { | |
| // https://issues.dlang.org/show_bug.cgi?id=17876 | |
| bool less1(immutable size_t[][] a, size_t[][] b) { return a < b; } | |
| bool less2(const void[][] a, void[][] b) { return a < b; } | |
| bool less3(inout size_t[][] a, size_t[][] b) { return a < b; } | |
| immutable size_t[][] a = [[1, 2], [3, 4]]; | |
| size_t[][] b = [[1, 2], [3, 5]]; | |
| assert(less1(a, b)); | |
| assert(less3(a, b)); | |
| auto va = [cast(immutable void[])a[0], a[1]]; | |
| auto vb = [cast(void[])b[0], b[1]]; | |
| assert(less2(va, vb)); | |
| } | |
| // objects | |
| @safe unittest | |
| { | |
| class C | |
| { | |
| int i; | |
| this(int i) { this.i = i; } | |
| override int opCmp(Object c) const @safe | |
| { | |
| return i - (cast(C)c).i; | |
| } | |
| } | |
| auto c1 = new C(1); | |
| auto c2 = new C(2); | |
| assert(__cmp(c1, null) > 0); | |
| assert(__cmp(null, c1) < 0); | |
| assert(__cmp(c1, c1) == 0); | |
| assert(__cmp(c1, c2) < 0); | |
| assert(__cmp(c2, c1) > 0); | |
| assert(__cmp([c1, c1][], [c2, c2][]) < 0); | |
| assert(__cmp([c2, c2], [c1, c1]) > 0); | |
| } | |
| // structs | |
| @safe unittest | |
| { | |
| struct C | |
| { | |
| ubyte i; | |
| this(ubyte i) { this.i = i; } | |
| } | |
| auto c1 = C(1); | |
| auto c2 = C(2); | |
| assert(__cmp([c1, c1][], [c2, c2][]) < 0); | |
| assert(__cmp([c2, c2], [c1, c1]) > 0); | |
| assert(__cmp([c2, c2], [c2, c1]) > 0); | |
| } | |
| // Compiler hook into the runtime implementation of array (vector) operations. | |
| template _arrayOp(Args...) | |
| { | |
| import core.internal.arrayop; | |
| alias _arrayOp = arrayOp!Args; | |
| } | |
| // Helper functions | |
| private inout(TypeInfo) getElement(inout TypeInfo value) @trusted pure nothrow | |
| { | |
| TypeInfo element = cast() value; | |
| for (;;) | |
| { | |
| if (auto qualified = cast(TypeInfo_Const) element) | |
| element = qualified.base; | |
| else if (auto redefined = cast(TypeInfo_Enum) element) | |
| element = redefined.base; | |
| else if (auto staticArray = cast(TypeInfo_StaticArray) element) | |
| element = staticArray.value; | |
| else if (auto vector = cast(TypeInfo_Vector) element) | |
| element = vector.base; | |
| else | |
| break; | |
| } | |
| return cast(inout) element; | |
| } | |
| private size_t getArrayHash(in TypeInfo element, in void* ptr, in size_t count) @trusted nothrow | |
| { | |
| if (!count) | |
| return 0; | |
| const size_t elementSize = element.tsize; | |
| if (!elementSize) | |
| return 0; | |
| static bool hasCustomToHash(in TypeInfo value) @trusted pure nothrow | |
| { | |
| const element = getElement(value); | |
| if (const struct_ = cast(const TypeInfo_Struct) element) | |
| return !!struct_.xtoHash; | |
| return cast(const TypeInfo_Array) element | |
| || cast(const TypeInfo_AssociativeArray) element | |
| || cast(const ClassInfo) element | |
| || cast(const TypeInfo_Interface) element; | |
| } | |
| import core.internal.traits : externDFunc; | |
| if (!hasCustomToHash(element)) | |
| return hashOf(ptr[0 .. elementSize * count]); | |
| size_t hash = 0; | |
| foreach (size_t i; 0 .. count) | |
| hash = hashOf(element.getHash(ptr + i * elementSize), hash); | |
| return hash; | |
| } | |
| /// Provide the .dup array property. | |
| @property auto dup(T)(T[] a) | |
| if (!is(const(T) : T)) | |
| { | |
| import core.internal.traits : Unconst; | |
| static assert(is(T : Unconst!T), "Cannot implicitly convert type "~T.stringof~ | |
| " to "~Unconst!T.stringof~" in dup."); | |
| // wrap unsafe _dup in @trusted to preserve @safe postblit | |
| static if (__traits(compiles, (T b) @safe { T a = b; })) | |
| return _trustedDup!(T, Unconst!T)(a); | |
| else | |
| return _dup!(T, Unconst!T)(a); | |
| } | |
| /// ditto | |
| // const overload to support implicit conversion to immutable (unique result, see DIP29) | |
| @property T[] dup(T)(const(T)[] a) | |
| if (is(const(T) : T)) | |
| { | |
| // wrap unsafe _dup in @trusted to preserve @safe postblit | |
| static if (__traits(compiles, (T b) @safe { T a = b; })) | |
| return _trustedDup!(const(T), T)(a); | |
| else | |
| return _dup!(const(T), T)(a); | |
| } | |
| /// Provide the .idup array property. | |
| @property immutable(T)[] idup(T)(T[] a) | |
| { | |
| static assert(is(T : immutable(T)), "Cannot implicitly convert type "~T.stringof~ | |
| " to immutable in idup."); | |
| // wrap unsafe _dup in @trusted to preserve @safe postblit | |
| static if (__traits(compiles, (T b) @safe { T a = b; })) | |
| return _trustedDup!(T, immutable(T))(a); | |
| else | |
| return _dup!(T, immutable(T))(a); | |
| } | |
| /// ditto | |
| @property immutable(T)[] idup(T:void)(const(T)[] a) | |
| { | |
| return a.dup; | |
| } | |
| private U[] _trustedDup(T, U)(T[] a) @trusted | |
| { | |
| return _dup!(T, U)(a); | |
| } | |
| private U[] _dup(T, U)(T[] a) // pure nothrow depends on postblit | |
| { | |
| if (__ctfe) | |
| { | |
| static if (is(T : void)) | |
| assert(0, "Cannot dup a void[] array at compile time."); | |
| else | |
| { | |
| U[] res; | |
| foreach (ref e; a) | |
| res ~= e; | |
| return res; | |
| } | |
| } | |
| import core.stdc.string : memcpy; | |
| void[] arr = _d_newarrayU(typeid(T[]), a.length); | |
| memcpy(arr.ptr, cast(const(void)*)a.ptr, T.sizeof * a.length); | |
| auto res = *cast(U[]*)&arr; | |
| static if (!is(T : void)) | |
| _doPostblit(res); | |
| return res; | |
| } | |
| private extern (C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow; | |
| /************** | |
| * Get the postblit for type T. | |
| * Returns: | |
| * null if no postblit is necessary | |
| * function pointer for struct postblits | |
| * delegate for class postblits | |
| */ | |
| private auto _getPostblit(T)() @trusted pure nothrow @nogc | |
| { | |
| // infer static postblit type, run postblit if any | |
| static if (is(T == struct)) | |
| { | |
| import core.internal.traits : Unqual; | |
| // use typeid(Unqual!T) here to skip TypeInfo_Const/Shared/... | |
| alias _PostBlitType = typeof(function (ref T t){ T a = t; }); | |
| return cast(_PostBlitType)typeid(Unqual!T).xpostblit; | |
| } | |
| else if ((&typeid(T).postblit).funcptr !is &TypeInfo.postblit) | |
| { | |
| alias _PostBlitType = typeof(delegate (ref T t){ T a = t; }); | |
| return cast(_PostBlitType)&typeid(T).postblit; | |
| } | |
| else | |
| return null; | |
| } | |
| private void _doPostblit(T)(T[] arr) | |
| { | |
| // infer static postblit type, run postblit if any | |
| if (auto postblit = _getPostblit!T()) | |
| { | |
| foreach (ref elem; arr) | |
| postblit(elem); | |
| } | |
| } | |
| unittest | |
| { | |
| static struct S1 { int* p; } | |
| static struct S2 { @disable this(); } | |
| static struct S3 { @disable this(this); } | |
| int dg1() pure nothrow @safe | |
| { | |
| { | |
| char[] m; | |
| string i; | |
| m = m.dup; | |
| i = i.idup; | |
| m = i.dup; | |
| i = m.idup; | |
| } | |
| { | |
| S1[] m; | |
| immutable(S1)[] i; | |
| m = m.dup; | |
| i = i.idup; | |
| static assert(!is(typeof(m.idup))); | |
| static assert(!is(typeof(i.dup))); | |
| } | |
| { | |
| S3[] m; | |
| immutable(S3)[] i; | |
| static assert(!is(typeof(m.dup))); | |
| static assert(!is(typeof(i.idup))); | |
| } | |
| { | |
| shared(S1)[] m; | |
| m = m.dup; | |
| static assert(!is(typeof(m.idup))); | |
| } | |
| { | |
| int[] a = (inout(int)) { inout(const(int))[] a; return a.dup; }(0); | |
| } | |
| return 1; | |
| } | |
| int dg2() pure nothrow @safe | |
| { | |
| { | |
| S2[] m = [S2.init, S2.init]; | |
| immutable(S2)[] i = [S2.init, S2.init]; | |
| m = m.dup; | |
| m = i.dup; | |
| i = m.idup; | |
| i = i.idup; | |
| } | |
| return 2; | |
| } | |
| enum a = dg1(); | |
| enum b = dg2(); | |
| assert(dg1() == a); | |
| assert(dg2() == b); | |
| } | |
| unittest | |
| { | |
| static struct Sunpure { this(this) @safe nothrow {} } | |
| static struct Sthrow { this(this) @safe pure {} } | |
| static struct Sunsafe { this(this) @system pure nothrow {} } | |
| static assert( __traits(compiles, () { [].dup!Sunpure; })); | |
| static assert(!__traits(compiles, () pure { [].dup!Sunpure; })); | |
| static assert( __traits(compiles, () { [].dup!Sthrow; })); | |
| static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; })); | |
| static assert( __traits(compiles, () { [].dup!Sunsafe; })); | |
| static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; })); | |
| static assert( __traits(compiles, () { [].idup!Sunpure; })); | |
| static assert(!__traits(compiles, () pure { [].idup!Sunpure; })); | |
| static assert( __traits(compiles, () { [].idup!Sthrow; })); | |
| static assert(!__traits(compiles, () nothrow { [].idup!Sthrow; })); | |
| static assert( __traits(compiles, () { [].idup!Sunsafe; })); | |
| static assert(!__traits(compiles, () @safe { [].idup!Sunsafe; })); | |
| } | |
| unittest | |
| { | |
| static int*[] pureFoo() pure { return null; } | |
| { char[] s; immutable x = s.dup; } | |
| { immutable x = (cast(int*[])null).dup; } | |
| { immutable x = pureFoo(); } | |
| { immutable x = pureFoo().dup; } | |
| } | |
| unittest | |
| { | |
| auto a = [1, 2, 3]; | |
| auto b = a.dup; | |
| debug(SENTINEL) {} else | |
| assert(b.capacity >= 3); | |
| } | |
| unittest | |
| { | |
| // Bugzilla 12580 | |
| void[] m = [0]; | |
| shared(void)[] s = [cast(shared)1]; | |
| immutable(void)[] i = [cast(immutable)2]; | |
| s = s.dup; | |
| static assert(is(typeof(s.dup) == shared(void)[])); | |
| m = i.dup; | |
| i = m.dup; | |
| i = i.idup; | |
| i = m.idup; | |
| i = s.idup; | |
| i = s.dup; | |
| static assert(!__traits(compiles, m = s.dup)); | |
| } | |
| unittest | |
| { | |
| // Bugzilla 13809 | |
| static struct S | |
| { | |
| this(this) {} | |
| ~this() {} | |
| } | |
| S[] arr; | |
| auto a = arr.dup; | |
| } | |
| unittest | |
| { | |
| // Bugzilla 16504 | |
| static struct S | |
| { | |
| __gshared int* gp; | |
| int* p; | |
| // postblit and hence .dup could escape | |
| this(this) { gp = p; } | |
| } | |
| int p; | |
| scope arr = [S(&p)]; | |
| auto a = arr.dup; // dup does escape | |
| } | |
| // compiler frontend lowers dynamic array comparison to this | |
| bool __ArrayEq(T1, T2)(T1[] a, T2[] b) | |
| { | |
| if (a.length != b.length) | |
| return false; | |
| foreach (size_t i; 0 .. a.length) | |
| { | |
| if (a[i] != b[i]) | |
| return false; | |
| } | |
| return true; | |
| } | |
| // compiler frontend lowers struct array postblitting to this | |
| void __ArrayPostblit(T)(T[] a) | |
| { | |
| foreach (ref T e; a) | |
| e.__xpostblit(); | |
| } | |
| // compiler frontend lowers dynamic array deconstruction to this | |
| void __ArrayDtor(T)(T[] a) | |
| { | |
| foreach_reverse (ref T e; a) | |
| e.__xdtor(); | |
| } |